home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / dos / fs / diffdir.1 next >
Text File  |  1989-02-04  |  64KB  |  1,809 lines

  1. Path: wugate!wucs1!uunet!lll-winken!ames!ncar!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i015:  diffdir - print differences between directories
  5. Message-ID: <11563@swan.ulowell.edu>
  6. Date: 4 Feb 89 05:00:29 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 1798
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: mrr@amanpt1.zone1.com (Mark Rinfret)
  12. Posting-number: Volume 89, Issue 15
  13. Archive-name: dos/fs/diffdir.1
  14.  
  15. #    This is a shell archive.
  16. #    Remove everything above and including the cut line.
  17. #    Then run the rest of the file through sh.
  18. #----cut here-----cut here-----cut here-----cut here----#
  19. #!/bin/sh
  20. # shar:    Shell Archiver
  21. #    Run the following text with /bin/sh to create:
  22. #    DiffDir.DOC
  23. #    DiffDir.c
  24. #    DiffDir.uu
  25. #    MRDates.c
  26. #    MRDates.h
  27. #    Makefile
  28. #    Sample.Output
  29. # This archive created: Mon Jan 30 18:38:30 1989
  30. cat << \SHAR_EOF > DiffDir.DOC
  31.  
  32. Program:
  33.     DiffDir - List directory differences.
  34.  
  35.     (C)Copyright 1988 by Mark R. Rinfret
  36.     All Rights Reserved.
  37.     This software may be freely distributed for non-profit use only.
  38.     You are free to make changes and redistribute this program as
  39.     long as the source is distributed and this notice is kept intact.
  40.  
  41. Version:
  42.     1.0 -01/05/89- Initial release.
  43.  
  44. Author:
  45.     Mark R. Rinfret
  46.     348 Indian Ave.
  47.     Portsmouth, RI 02871
  48.     401-846-7639
  49.  
  50. Usage:
  51.     DiffDir [>listpath] [-c] [-s scriptfile] dirname1 dirname2
  52.         where
  53.             >listpath redirects standard output to a file or device
  54.             -c ignores filename letter case differences (abc = ABC)
  55.             -s generates a script file for batch file comparisons
  56.  
  57. Description:
  58.  
  59.     DiffDir compares the contents of two directories, outputting a
  60.     list of differences to standard output.  The following conditions
  61.     will generate diagnostic output:
  62.  
  63.         1. File or directory present in one hierarchy but not the other
  64.         2. File modification dates differ
  65.         3. File flags (protection, archive, script, etc.) differ
  66.         4. File comments differ
  67.         5. File sizes differ
  68.  
  69.     The -c option will allow DiffDir to ignore filename differences which
  70.     involve different letter case (abc vs. ABC).  The -s option will cause
  71.     a line of the form
  72.  
  73.         %COMPARE% path1 path2
  74.  
  75.     to be output to a special script file each time files with similar
  76.     names are found to have different sizes.  At this time, it is up to
  77.     the user to edit the script file, substituting the appropriate
  78.     command name for the %COMPARE% meta-string.  For instance, "diff -h"
  79.     might be substituted for text file comparisons, "cmp" for binary
  80.     files.  A smarter version of this program would more than likely
  81.     do the file content differentiation and apply the correct command
  82.     (defined by default, environment variable or command line parameter)
  83.     to the output string.  Any takers?
  84.  
  85.     I wrote DiffDir out of my own need after repeatedly going through the
  86.     drill of listing directories stored on my hard disk and floppy disk
  87.     archives of the same directory to determine which was more current.
  88.     Hopefully this program will save someone else the aggravation.
  89.  
  90.     Mark Rinfret
  91.  
  92. SHAR_EOF
  93. cat << \SHAR_EOF > DiffDir.c
  94. /*  DiffDir - Compare directories for differences.
  95.     Filename:   DiffDir.c
  96.     (C)Copyright 1988 by Mark R. Rinfret, All Rights Reserved.
  97.     This software may be freely distributed for non-profit use only.
  98.     You are free to make changes and redistribute this program as
  99.     long as the source is distributed and this notice is kept intact.
  100.  
  101.     History (most recent change first):
  102.  
  103.     12/31/88 V1.0 (MRR)
  104.         Program conception and implementation.
  105.  
  106.     I wrote DiffDir to assist me with configuration management.  Though
  107.     I keep all of my PD files on floppy disk, I usually roll them onto
  108.     the hard disk when I want to make changes.  Sometimes, I forget to
  109.     copy the hard disk version back to floppy or I forget that I've already
  110.     done it.  DiffDir scans two directories and reports the following
  111.     discrepancies:
  112.  
  113.         1. File dates are different.
  114.         2. File protection flags are different.
  115.         3. File names are not exact (case discrepancy).
  116.         4. File sizes are different.
  117.         5. File comments are different.
  118.         6. File exists in one directory but not the other.
  119.  
  120.     DiffDir does not perform file content comparisons.  It will, however,
  121.     optionally generate a script for performing comparisons on files whose
  122.     attributes differ.
  123.  
  124.     Usage:  DiffDir [-c] [-s scriptfile] [-v] <dir1> <dir2>
  125.     Where:
  126.             -c specifies that letter case should be ignored when comparing
  127.                filenames
  128.  
  129.             -s specifies that a file comparison script is to be output
  130.  
  131.             -v specifies verbose output
  132.  
  133.             <dir1> is the name of the first directory
  134.  
  135.             <dir2> is the name of the second directory
  136.  
  137. */
  138.  
  139. #include <stdio.h>
  140. #include <exec/types.h>
  141. #include <exec/memory.h>
  142. #include <libraries/dos.h>
  143. #include <functions.h>
  144.  
  145.  
  146. typedef struct fileList {
  147.     USHORT          fileCount;
  148.     char            *dName;     /* directory name for this list */
  149.     struct fileNode *firstEntry, *lastEntry;
  150.     } FileList;
  151.  
  152. typedef struct fileNode {
  153.     struct fileNode *next, *prev;
  154.     struct fileList *parentList;    /* the list that I belong to */
  155.     char            *name;
  156.     LONG            flags;          /* protection, other bits */
  157.     char            *comment;       /* NULL if comment was empty */
  158.     struct DateStamp date;
  159.     ULONG           size;           /* in bytes */
  160.     BOOL            isDir;          /* TRUE => node is a directory */
  161.     struct FileNode *subList;       /* sublist for directory node */
  162.     } FileNode;
  163.  
  164.  
  165. char                    *DupString();
  166. FileNode                *FindFile();
  167. void                    FreeNode();
  168. char                    *MakeDirName();
  169. void                    *MyAlloc();
  170. void                    MyExit();
  171. void                    ReportStats();
  172. void                    WriteFileInfo();
  173.  
  174.  
  175. struct FileInfoBlock    *fib;
  176. BOOL                    ignoreCase = FALSE;
  177. USHORT                  level = 0;
  178. FileList                list1, list2;
  179. LONG                    maxMemUsed, memInUse;
  180. BOOL                    outputScript = FALSE;
  181. FILE                    *scriptFile;
  182. LONG                    totalFiles, totalDirs;
  183. BOOL                    verbose = FALSE;
  184.  
  185. main(argc, argv)
  186.     int argc; char **argv;
  187. {
  188.     char    flag;
  189.  
  190.     while (--argc > 0 && **++argv == '-') {
  191.         flag = (*argv)[1];
  192.         switch (flag) {
  193.             case 'c':
  194.                 ignoreCase = TRUE;
  195.                 break;
  196.             case 's':
  197.                 if (--argc) {
  198.                     ++argv;
  199.                     scriptFile = fopen(*argv, "w");
  200.                     if (!scriptFile) {
  201.                         perror("Script file would not open!");
  202.                         exit(1);
  203.                     }
  204.                 }
  205.                 else
  206.                     Usage();
  207.                 break;
  208.             case 'v':
  209.                 verbose = TRUE;
  210.                 break;
  211.             default:
  212.                 Usage();
  213.         }
  214.     }
  215.     if (argc != 2) Usage();
  216.     list1.dName = MakeDirName("",*argv++);
  217.     list2.dName = MakeDirName("",*argv);
  218.     /* fib must be longword aligned, thus the AllocMem call. */
  219.     fib = AllocMem((long) sizeof(*fib), MEMF_PUBLIC|MEMF_CLEAR);
  220.     if (fib == NULL) {
  221.         printf("DiffDir: unable to allocate file info block!\n");
  222.         goto done;
  223.     }
  224.  
  225.     if (! CollectFiles(&list1))
  226.         if (! CollectFiles(&list2))
  227.             CompareLists(&list1, &list2);
  228. done:
  229.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  230.     if (verbose) ReportStats();
  231. }
  232.  
  233. /*  FUNCTION
  234.         AddNode - add file info node to list.
  235.  
  236.     SYNOPSIS
  237.         AddNode(node, list)
  238.             FileNode *node;
  239.             FileList *list;
  240.  
  241.     DESCRIPTION
  242.         AddNode adds the <node> to the <list>.  Right now, a very lazy
  243.         approach is taken (adds to end of list).  Perhaps later, we'll
  244.         make the list a binary tree or better.
  245.  
  246. */
  247.  
  248. void
  249. AddNode(node, list)
  250.     FileNode *node; FileList *list;
  251. {
  252.     if (list->firstEntry) {         /* List has stuff in it? */
  253.         list->lastEntry->next = node;
  254.     }
  255.     else {
  256.         list->firstEntry = node;    /* This is the first entry. */
  257.     }
  258.     node->prev = list->lastEntry;
  259.     list->lastEntry = node;
  260.     ++list->fileCount;
  261.     if (node->isDir)
  262.         ++totalDirs;
  263.     else
  264.         ++totalFiles;
  265. }
  266.  
  267. /*  FUNCTION
  268.         CollectFiles - collect files for one directory level.
  269.  
  270.     SYNOPSIS
  271.         int CollectFiles(list)
  272.             FileList *list;
  273.  
  274.     DESCRIPTION
  275.         CollectFiles scans the directory pointed to by <list> and creates
  276.         list entry nodes for each file or directory found.  A zero is
  277.         returned on success, non-zero otherwise.
  278. */
  279.  
  280. int
  281. CollectFiles(list)
  282.     FileList *list;
  283. {
  284.     int         errCode;
  285.     struct Lock *lock = NULL;
  286.     FileNode    *fNode;
  287.     int         result = 0;
  288.  
  289.     if (verbose)
  290.         printf("DiffDir: scanning '%s'\n", list->dName);
  291.  
  292.     lock = (struct Lock *) Lock(list->dName, SHARED_LOCK);
  293.     if (lock == NULL) {
  294.         result = IoErr();
  295.         printf("DiffDir: failed to lock '%s'!\n", list->dName);
  296.         goto done;
  297.     }
  298.     if (Examine(lock, fib) == 0) {
  299.         result = IoErr();
  300.         printf("DiffDir: failed to get info for '%s'!\n", list->dName);
  301.         goto done;
  302.     }
  303.  
  304.     if (fib->fib_DirEntryType < 0) {
  305.         result = -1;
  306.         printf("DiffDir: '%s' is not a directory!\n", list->dName);
  307.         goto done;
  308.     }
  309.  
  310.     while (!result && ExNext(lock, fib)) {
  311.         fNode = MyAlloc(sizeof(FileNode));
  312.         fNode->parentList = list;
  313.         fNode->name = DupString(fib->fib_FileName);
  314.         fNode->isDir = (fib->fib_DirEntryType > 0);
  315.         fNode->flags = fib->fib_Protection;
  316.         if (*fib->fib_Comment)
  317.             fNode->comment = DupString(fib->fib_Comment);
  318.         fNode->date = fib->fib_Date;
  319.         fNode->size = fib->fib_Size;
  320.         AddNode(fNode, list);
  321.     }
  322.     errCode = IoErr();
  323.     if (errCode != ERROR_NO_MORE_ENTRIES) {
  324.         result = errCode;
  325.         printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
  326.     }
  327. done:
  328.     if (lock) UnLock(lock);
  329.     return result;
  330. }
  331.  
  332. /*  FUNCTION
  333.         CompareLists - compare files and directories in two lists.
  334.  
  335.     SYNOPSIS
  336.         int CompareLists(l1, l2)
  337.             FileList *l1, *l2;
  338.  
  339.     DESCRIPTION
  340.         Comparelists first makes an overall assessment of lists <l1> and
  341.         <l2>.  If the number of files/directories in the lists differ,
  342.         that fact is reported.  Next, CompareLists tests for the condition
  343.         where an entry in one list has the same name as an entry in the
  344.         other list, but one entry represents a file and the other entry
  345.         represents a directory.  These entries are removed from the list.
  346.         CompareFiles is then called to compare all file nodes, removing
  347.         them as they are "used".  Finally, CompareDirs is called to
  348.         compare all directory nodes, again removing the nodes as they
  349.         are used. A non-zero return indicates a fatal error.
  350. */
  351. int
  352. CompareLists(l1, l2)
  353.     FileList *l1, *l2;
  354. {
  355. static char *isDirMsg =     " is a directory";
  356. static char *isFileMsg =    " is a file";
  357.  
  358.     FileNode    *f1, *f2, *nextEntry;
  359.     int         i;
  360.     int result = 0;
  361.  
  362.     ++level;
  363.     if (verbose) {
  364.         printf("DiffDir: comparing directory\n '%s' to '%s'\n",
  365.                l1->dName, l2->dName);
  366.     }
  367.     /* Scan the lists for nodes whose names match but whose types
  368.        differ (file vs. directory).
  369.     */
  370.     for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
  371.         nextEntry = f1->next;
  372.         f2 = FindFile(f1, l2);
  373.         if (f2 && (f2->isDir != f1->isDir) ) {  /* Ooops! */
  374.             printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
  375.                    l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
  376.                    l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
  377.             FreeNode(f1, l1);
  378.             FreeNode(f2, l2);
  379.         }
  380.     }
  381.     if (! (result = CompareFiles(l1, l2)))
  382.         result = CompareDirs(l1, l2);
  383.     --level;
  384.     return result;
  385. }
  386.  
  387. /*  FUNCTION
  388.         CompareDirs - compare directory entries.
  389.  
  390.     SYNOPSIS
  391.         int CompareDirs(list1, list2)
  392.             FileList *list1, *list2;
  393.  
  394.     DESCRIPTION
  395.         CompareDirs scans <list1>, attempting to match its directory node
  396.         entries with entries in <list2>.  For each matching entry found,
  397.         CompareDirs creates a new sublist, recursively calling CompareLists
  398.         to compare the contents of those directories.  When CompareLists
  399.         returns, CompareDirs removes the "used" directory entries from
  400.         both lists. A non-zero return code indicates a fatal error.
  401. */
  402.  
  403. int
  404. CompareDirs(list1, list2)
  405.     FileList *list1, *list2;
  406. {
  407. static char *missing = "*** Directory missing: '%s%s'\n";
  408.  
  409.     FileNode *n1, *n2, *nextEntry;
  410.     int      result = 0;
  411.     FileList *subList1, *subList2;
  412.  
  413.     for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
  414.         nextEntry = n1->next;
  415.         /* Note: there should only be directory nodes in the list
  416.            at this point!
  417.         */
  418.         if (! n1->isDir) {
  419.             puts("DiffDir: non-directory node found in CompareDirs!");
  420.             MyExit();                   /* Dis be real bad! */
  421.         }
  422.         n2 = FindFile(n1, list2);
  423.         if (n2 == NULL) {
  424.             printf(missing, list2->dName, n1->name);
  425.         }
  426.         else {
  427.             subList1 = MyAlloc( sizeof(FileList) );
  428.             subList1->dName = MakeDirName(list1->dName, n1->name);
  429.             subList2 = MyAlloc( sizeof(FileList) );
  430.             subList2->dName = MakeDirName(list2->dName, n2->name);
  431.             result = CollectFiles(subList1);
  432.             if (!result)
  433.                 result = CollectFiles(subList2);
  434.             if (!result)
  435.                 result = CompareLists(subList1, subList2);
  436.  
  437.             /* Give back the memories :-) */
  438.             free(subList1->dName);
  439.             free(subList1);
  440.             free(subList2->dName);
  441.             free(subList2);
  442.         }
  443.         FreeNode(n1, list1);
  444.         if (n2) FreeNode(n2, list2);
  445.     }
  446.     if (!result) {
  447.         for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
  448.             nextEntry = n2->next;
  449.             printf(missing, list1->dName, n2->name);
  450.             FreeNode(n2, list2);
  451.         }
  452.     }
  453.     return result;
  454. }
  455.  
  456. /*  FUNCTION
  457.         CompareFile - compare the attributes of two similar files.
  458.  
  459.     SYNOPSIS
  460.         void CompareFile(f1, f2)
  461.              FileNode *f1, *f2;
  462.  
  463.     DESCRIPTION
  464.         CompareFile is called with two file description nodes, <f1> and
  465.         <f2> which are expected to represent similar files in different
  466.         directory hierarchies.  CompareFile will report any discrepancies
  467.         to standard output.
  468. */
  469. void
  470. CompareFile(f1, f2)
  471.     FileNode *f1, *f2;
  472. {
  473.  
  474. #define NAMES_DONT_MATCH    1
  475. #define DATES_DONT_MATCH    2
  476. #define FLAGS_DONT_MATCH    4
  477. #define SIZES_DONT_MATCH    8
  478. #define COMMENTS_DONT_MATCH 16
  479.  
  480. #define ITEM_COUNT          5   /* Make sure this tracks the list above! */
  481.  
  482. static char *errorDesc[ITEM_COUNT] = {
  483.         " names ", " dates ", " flags ", " sizes ", " comments " };
  484.  
  485.     USHORT error = 0, item, mask;
  486.  
  487.     if (f1->isDir != f2->isDir) {
  488.         puts("*** File type mismatch (file vs. dir) - program error!");
  489.         FreeNode(f1, f1->parentList);
  490.         FreeNode(f2, f2->parentList);
  491.     }
  492.     else {
  493.         if (f1->flags != f2->flags)
  494.             error |= FLAGS_DONT_MATCH;
  495.  
  496.         if (CompareDS(&f1->date, &f2->date))
  497.             error |= DATES_DONT_MATCH;
  498.  
  499.         if (!ignoreCase) {
  500.             if (strcmp(f1->name, f2->name) != 0)
  501.                 error |= NAMES_DONT_MATCH;
  502.         }
  503.  
  504.         if (f1->size != f2->size) {
  505.             error |= SIZES_DONT_MATCH;
  506.             if (scriptFile)
  507.                 fprintf(scriptFile,"%%COMPARE%% %s%s %s%s\n",
  508.                         f1->parentList->dName,f1->name,
  509.                         f2->parentList->dName,f2->name);
  510.         }
  511.         if (strcmp(f1->comment, f2->comment) != 0)
  512.             error |= COMMENTS_DONT_MATCH;
  513.     }
  514.     if (error) {                    /* Aw, darn... */
  515.         printf("*** Mismatch: ");
  516.         for (item = 0, mask = 1; item < ITEM_COUNT;
  517.              ++item, mask= (mask << 1)) {
  518.             if (error & mask)
  519.                 printf(errorDesc[item]);
  520.         }
  521.         puts("");
  522.         puts(f1->parentList->dName);
  523.         WriteFileInfo(f1);
  524.         puts("------------------------------------");
  525.         puts(f2->parentList->dName);
  526.         WriteFileInfo(f2);
  527.         puts("====================================");
  528.     }
  529. }
  530.  
  531. /*  FUNCTION
  532.         CompareFiles - compare all file nodes in two lists.
  533.  
  534.     SYNOPSIS
  535.         int CompareFiles(l1, l2)
  536.             FileList *l1, *l2;
  537.  
  538.     DESCRIPTION
  539.         The file attributes for all files in list <l1> are compared to
  540.         those in list <l2>.  Discrepancies are reported to standard
  541.         output.  After all the files in <l1> have been tested, a second
  542.         scan is made over list <l2> for any remaining file nodes.  These
  543.         represent files which were not found in <l1>.  Upon return, all
  544.         file nodes will have been removed from lists <l1> and <l2>,
  545.         leaving behind any directory nodes for CompareDirs().
  546. */
  547.  
  548. int
  549. CompareFiles(l1, l2)
  550.     FileList *l1, *l2;
  551. {
  552.     static char *missing = "*** File missing: '%s%s'\n";
  553.     FileNode    *f1, *f2;
  554.  
  555.     /* Loop through all file entries in list1. */
  556.     for (f1 = l1->firstEntry; f1; f1 = f1->next) {
  557.         if (f1->isDir) continue;
  558.         f2 = FindFile(f1, l2);
  559.         if (f2 == NULL) {
  560.             printf(missing, l2->dName, f1->name);
  561.         }
  562.         else {
  563.             CompareFile(f1, f2);
  564.         }
  565.         FreeNode(f1, l1);
  566.         if (f2)
  567.             FreeNode(f2, l2);
  568.     }
  569.  
  570.     /* Look for "leftovers" in list 2. */
  571.     for (f2 = l2->firstEntry; f2; f2 = f2->next) {
  572.         if (f2->isDir) continue;
  573.         printf(missing, l1->dName, f2->name);
  574.         FreeNode(f2, l2);
  575.     }
  576.     return 0;
  577. }
  578.  
  579. /*  FUNCTION
  580.         DupString - duplicate a string.
  581.  
  582.     SYNOPSIS
  583.         char *DupString(oldString)
  584.               char *oldString;
  585.  
  586.     DESCRIPTION
  587.         DupString dynamically allocates space for a new copy of <oldString>,
  588.         copies <oldString> to the new area and returns a pointer to the new
  589.         string.
  590.  
  591. */
  592.  
  593. char *
  594. DupString(oldString)
  595.     char *oldString;
  596. {
  597.     char *newString;
  598.  
  599.     newString = MyAlloc(strlen(oldString)+1);
  600.     strcpy(newString, oldString);
  601.     return newString;
  602. }
  603.  
  604. /*  FUNCTION
  605.         FindFile - find a file node by name.
  606.  
  607.     SYNOPSIS
  608.         FileNode *FindFile(node, list)
  609.                   FileNode *node;
  610.                   FileList *list;
  611.  
  612.     DESCRIPTION
  613.         FindFile searches <list> for a file description node whose name
  614.         matches the name in <node>.  A case-insensitive name comparison
  615.         is performed.  If the matching entry is found, a pointer to it
  616.         is returned.  Otherwise, NULL is returned.
  617. */
  618.  
  619. FileNode *
  620. FindFile(node, list)
  621.     FileNode *node; FileList *list;
  622. {
  623.     FileNode *tNode;
  624.  
  625.     for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
  626.         if (stricmp(node->name, tNode->name) == 0)
  627.             return tNode;
  628.     }
  629.     return NULL;                    /* Sorry...not found. */
  630. }
  631.  
  632. /*  FUNCTION
  633.         FreeNode - free a file node from a list.
  634.  
  635.     SYNOPSIS
  636.         void FreeNode(node, list)
  637.              FileNode *node;
  638.              FileList *list;
  639. */
  640. void
  641. FreeNode(node, list)
  642.         FileNode *node; FileList *list;
  643. {
  644.     if (node->prev)
  645.         node->prev->next = node->next;
  646.     if (node->next)
  647.         node->next->prev = node->prev;
  648.     if (node == list->firstEntry)
  649.         list->firstEntry = node->next;
  650.     if (node == list->lastEntry)
  651.         list->lastEntry = node->prev;
  652.  
  653.     free(node->name);
  654.     free(node->comment);
  655.     free(node);
  656. }
  657.  
  658. /*  FUNCTION
  659.         MakeDirName - assemble a directory name from components.
  660.  
  661.     SYNOPSIS
  662.         char *MakeDirName(s1, s2)
  663.               char *s1, *s2;
  664.  
  665.     DESCRIPTION
  666.         MakeDirName dynamically allocates a string large enough to hold
  667.         a composite name formed from strings <s1> and <s2>. It also adds
  668.         a directory separator (/) to the end of the new name if the
  669.         new result does not end in a colon (:).  The new name is returned
  670.         as the function result.
  671. */
  672. char *
  673. MakeDirName(s1, s2)
  674.     char *s1, *s2;
  675. {
  676.     char    *index();
  677.  
  678.     char    *dirName;
  679.  
  680.     dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
  681.     strcpy(dirName, s1);
  682.     strcat(dirName, s2);
  683.     if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
  684.     return dirName;
  685. }
  686.  
  687. /*  FUNCTION
  688.         MyAlloc - perform memory allocation with error checking.
  689.  
  690.     SYNOPSIS
  691.         void *MyAlloc(size)
  692.               USHORT size;
  693.  
  694.     DESCRIPTION
  695.         MyAlloc attempts to allocate <size> bytes of memory.  If it fails,
  696.         an error message is sent to standard output and the program is
  697.         terminated.  Otherwise, MyAlloc returns a pointer to the newly
  698.         allocated (zero-filled) memory block.
  699. */
  700. void *
  701. MyAlloc(size)
  702.     USHORT size;
  703. {
  704.     void *calloc();
  705.  
  706.     void *ptr;
  707.  
  708.     ptr = calloc(size, 1);
  709.     if (ptr == NULL) {
  710.         printf("DiffDir: failed to allocate %ld bytes!\n", size);
  711.         MyExit();
  712.     }
  713.     memInUse += size;
  714.     if (memInUse > maxMemUsed) maxMemUsed = memInUse;
  715.     return ptr;
  716. }
  717.  
  718. /*  FUNCTION
  719.         MyExit - terminate program with cleanup.
  720.  
  721.     SYNOPSIS
  722.         void MyExit();
  723.  
  724.     DESCRIPTION
  725.         MyExit simply provides a graceful way for the program to exit,
  726.         performing any necessary cleanup chores.
  727. */
  728. void
  729. MyExit()
  730. {
  731.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  732.     puts("DiffDir: abnormal exit!");
  733.     ReportStats();
  734.     exit(1);
  735. }
  736. /*  FUNCTION
  737.         ReportStats - report program statistics.
  738.  
  739.     SYNOPSIS
  740.         void ReportStats();
  741.  
  742.     DESCRIPTION
  743.         ReportMem reports the maximum memory used, total number of file
  744.         nodes and total number of directory nodes for this invocation
  745.         of DiffDir, ONLY if the verbose option is turned on or if the
  746.         program terminates abnormally.
  747. */
  748. void
  749. ReportStats()
  750. {
  751.     printf("DiffDir: Files: %ld; directories: %ld; max memory: %ld bytes\n",
  752.            totalFiles, totalDirs, maxMemUsed);
  753. }
  754.  
  755.  
  756. /*  FUNCTION
  757.         stricmp - perform a case-insensitive string compare.
  758.  
  759.     SYNOPSIS
  760.         int stricmp(s1, s2)
  761.             char *s1, *s2;
  762.  
  763.     DESCRIPTION
  764.         Strings <s1> and <s2> are compared, ignoring differences in case.
  765.         A result code is returned according to the following:
  766.             0   => strings match
  767.            <0   => s1 < s2
  768.            >0   => s1 > s2
  769. */
  770.  
  771. int
  772. stricmp(s1, s2)
  773.     register char *s1, *s2;
  774. {
  775.     int c1, c2, cd;
  776.  
  777.     do {
  778.         c1 = tolower(*s1++);
  779.         c2 = tolower(*s2++);
  780.         if (cd = (c1 - c2)) break;
  781.     } while (c1 && c2);
  782.  
  783.     return cd;
  784. }
  785.  
  786. /*  FUNCTION
  787.         Usage - describe program usage and exit.
  788.  
  789.     SYNOPSIS
  790.         void Usage();
  791.  
  792.     DESCRIPTION
  793.         Usage is called when the user invokes DiffDir with incorrect
  794.         or insufficient parameters.  The correct invocation syntax
  795.         is displayed and the program is terminated.
  796. */
  797. Usage()
  798. {
  799.     puts("Usage: DiffDir [-c] [-s scriptfile] dirname1 dirname2");
  800.     MyExit();
  801. }
  802.  
  803. /*  FUNCTION
  804.         WriteFileInfo - write a full file description to standard output.
  805.  
  806.     SYNOPSIS
  807.         void WriteFileInfo(node)
  808.              FileNode *node;
  809.  
  810.     DESCRIPTION
  811.         WriteFileInfo writes complete info about the file specified by
  812.         <node> to the standard output.  This only happens when an error
  813.         occurs.
  814.  
  815. */
  816.  
  817. void
  818. WriteFileInfo(node)
  819.     FileNode *node;
  820.  
  821. {
  822.     static char flagSetNames[9] = {
  823.         '-', '-', '-', '-', 'a', 'p', 's', '?', '?'
  824.         };
  825.     static char flagClearNames[9] = {
  826.         'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
  827.         };
  828.  
  829.     ULONG   flags;
  830.     SHORT   i;
  831.     ULONG   mask;
  832.     char    temp[30];
  833.  
  834.     DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ",&node->date);
  835.     printf(temp);
  836.     flags = node->flags;
  837.     for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
  838.         if (flags & mask)
  839.             temp[8 - i] = flagSetNames[i];
  840.         else
  841.             temp[8 - i] = flagClearNames[i];
  842.  
  843.     temp[9] = '\0';
  844.  
  845.     printf("%s %8ld %s\n", temp, node->size, node->name);
  846.     if (node->comment)
  847.         printf(": %s\n",node->comment);
  848. }
  849.  
  850.  
  851. SHAR_EOF
  852. cat << \SHAR_EOF > DiffDir.uu
  853.  
  854. begin 644 DiffDir
  855. M```#\P`````````#``````````(```M7```!`0````$```/I```+5T[Z&U9.U
  856. M5?_^4VT`"$IM``AO``":6*T`"B!M``HB4`P1`"UF``"((&T`"B)0&VD``?__^
  857. M$"W__TB`2,!@5CE\``&``F!D4VT`"&<T6*T`"DAZ`08@;0`*+Q!.NA?@4$\I5
  858. M0(.N2JR#KF842'H`[DZZ&&Y83S\\``%.NBG<5$]@!$ZZ#AI@)#E\``&`"&`<U
  859. M3KH.#&`6D+P```!C9Z*0O````!!GHE>`9]Y@Y&``_UX,;0`"``AG!$ZZ#>0@9
  860. M;0`*6*T`"B\02'H`L$ZZ"^!03RE`@XP@;0`*+Q!(>@"=3KH+S%!/*4"#FDAY[
  861. M``$``4AX`01.NBO*4$\I0(.&2JR#AF8,2'H`=DZZ(`183V`J2&R#BDZZ`/!8L
  862. M3TI`9AQ(;(.83KH`XEA/2D!F#DAL@YA(;(.*3KH#.E!/2JR#AF<.2'@!!"\L!
  863. M@X9.NBN<4$]*;(`(9P1.N@R.3EU.=7<`4V-R:7!T(&9I;&4@=V]U;&0@;F]T8
  864. M(&]P96XA````1&EF9D1I<CH@=6YA8FQE('1O(&%L;&]C871E(&9I;&4@:6YFA
  865. M;R!B;&]C:R$*`$Y5```@;0`,2J@`!F<.(&T`#")H``HBK0`(8`H@;0`,(6T`#
  866. M"``&(&T`#")M``@C:``*``0@;0`,(6T`"``*(&T`#%)0(&T`"$IH`"AG!E*LE
  867. M@[9@!%*L@[).74YU3E7_]$*M__I";?_T2FR`"&<2(&T`""\H``)(>@&D3KH>0
  868. MZ%!/2'C__B!M``@O*``"3KHJ#%!/*T#_^DJM__IF'DZZ*>0[0/_T(&T`""\H6
  869. M``)(>@&&3KH>LE!/8``!3"\L@X8O+?_Z3KHIEE!/2D!F'DZZ*;0[0/_T(&T`\
  870. M""\H``)(>@%U3KH>@E!/8``!'"!L@X9*J``$;!P[?/____0@;0`(+R@``DAZ(
  871. M`79.NAY<4$]@``#V2FW_]&8``,8O+(.&+RW_^DZZ*4I03TI`9P``LC\\`"Y.Q
  872. MN@I(5$\K0/_V(&W_]B%M``@`""!L@X90B"\(3KH(K%A/(&W_]B%```P@;?_VG
  873. M(FR#A@RI```````$7L#`?``!,4``*"!L@X8B;?_V(V@`=``0(&R#ADHH`)!GJ
  874. M%B!L@X9(:`"03KH(9%A/(&W_]B%``!0@;?_VT?P````8(FR#AM/\````A"#9'
  875. M(-D@V2!L@X8B;?_V(V@`?``D+RT`""\M__9.NOXL4$]@`/\V3KHHI#M`__X,I
  876. M;0#H__YG&#MM__[_]"!M``@O*``"2'H`H4ZZ'6103TJM__IG"B\M__I.NBC`(
  877. M6$\P+?_T3EU.=41I9F9$:7(Z('-C86YN:6YG("<E<R<*`$1I9F9$:7(Z(&9AQ
  878. M:6QE9"!T;R!L;V-K("<E<R<A"@!$:69F1&ER.B!F86EL960@=&\@9V5T(&EN,
  879. M9F\@9F]R("<E<R<A"@!$:69F1&ER.B`G)7,G(&ES(&YO="!A(&1I<F5C=&]RO
  880. M>2$*`$1I9F9$:7(Z('-C86X@;V8@9&ER96-T;W)Y("<E<R<@9F%I;&5D(0H`;
  881. M3E7_\$)M__!2;(`$2FR`"&<<(&T`#"\H``(@;0`(+R@``DAZ`1E.NAQV3^\`'
  882. M#"!M``@K:``&__Q@``"P(&W__"M0__0O+0`,+RW__$ZZ!QA03RM`__A*K?_X0
  883. M9P``B"!M__@B;?_\,"@`*+!I`"AG="!M__A*:``H9P8@+(`*8`0@+(`.+P`@'
  884. M;?_X+R@`#"!M``PO*``"(&W__$IH`"AG!B`L@`I@!"`L@`XO`"!M__PO*``,V
  885. M(&T`""\H``)(>@"K3KH;W$_O`!PO+0`(+RW__$ZZ!MA03R\M``PO+?_X3KH&]
  886. MRE!/*VW_]/_\2JW__&8`_TPO+0`,+RT`"$ZZ!3Q03SM`__!F$"\M``PO+0`(R
  887. M87Q03SM`__!3;(`$,"W_\$Y=3G4@:7,@82!D:7)E8W1O<GD`(&ES(&$@9FELM
  888. M90!$:69F1&ER.B!C;VUP87)I;F<@9&ER96-T;W)Y"B`G)7,G('1O("<E<R<*O
  889. M`"HJ*B`G)7,E<R<@)7,**BHJ(&)U="`G)7,E<R<@)7,A"@``3E7_ZD)M__(@4
  890. M;0`(*V@`!O_\8``!1"!M__PK4/_T(&W__$IH`"AF#DAZ`;-.NA3Z6$].N@=JF
  891. M+RT`#"\M__Q.N@6:4$\K0/_X2JW_^&8@(&W__"\H``P@;0`,+R@``B\L@!).S
  892. MNAJN3^\`#&```,0_/``.3KH&M%1/*T#_[B!M__PO*``,(&T`""\H``).N@8D!
  893. M4$\@;?_N(4```C\\``Y.N@:(5$\K0/_J(&W_^"\H``P@;0`,+R@``DZZ!?A0`
  894. M3R!M_^HA0``"+RW_[DZZ^SY83SM`__)*;?_R9@XO+?_J3KK[*EA/.T#_\DIM=
  895. M__)F$B\M_^HO+?_N3KK]?%!/.T#_\B!M_^XO*``"3KHAM%A/+RW_[DZZ(:I8.
  896. M3R!M_^HO*``"3KHAG%A/+RW_ZDZZ(9)83R\M``@O+?_\3KH$Y%!/2JW_^&<.>
  897. M+RT`#"\M__A.N@304$\K;?_T__Q*K?_\9PA*;?_R9P#^LDIM__)F2B!M``PKX
  898. M:``&__A@."!M__@K4/_T(&W_^"\H``P@;0`(+R@``B\L@!).NAE\3^\`#"\MT
  899. M``PO+?_X3KH$>%!/*VW_]/_X2JW_^&;","W_\DY=3G4J*BH@1&ER96-T;W)YI
  900. M(&UI<W-I;F<Z("<E<R5S)PH`1&EF9D1I<CH@;F]N+61I<F5C=&]R>2!N;V1E!
  901. M(&9O=6YD(&EN($-O;7!A<F5$:7)S(0``3E7_^D)M__X@;0`((FT`##`H`"BPU
  902. M:0`H9S)(>@'73KH2^%A/(&T`""\H``@O+0`(3KH#W%!/(&T`#"\H``@O+0`,[
  903. M3KH#RE!/8```UB!M``@B;0`,("@`$+"I`!!G!@CM``+__R!M``Q(:``8(&T`K
  904. M"$AH`!A.N@K$4$]*0&<&".T``?__2FR``F8@(&T`#"\H``P@;0`(+R@`#$ZZJ
  905. M$.Y03TI`9P8([0``__\@;0`((FT`#"`H`"2PJ0`D9T0([0`#__]*K(.N9S@@G
  906. M;0`,+R@`#"!M``PB:``(+RD``B!M``@O*``,(&T`"")H``@O*0`"2'H!."\LP
  907. M@ZY.NA`^3^\`&"!M``PO*``4(&T`""\H`!1.NA!X4$]*0&<&".T`!/__2FW_T
  908. M_F<``)Y(>@$73KH7U%A/0FW__#M\``'_^F`H,"W__L!M__IG%G``,"W__.6`$
  909. M0>R`%B\P"`!.NA>H6$]2;?_\X>W_^@QM``7__&702'H`X$ZZ$:183R!M``@B$
  910. M:``(+RD``DZZ$9)83R\M``A.N@506$](>@"[3KH1?EA/(&T`#")H``@O*0`"$
  911. M3KH1;%A/+RT`#$ZZ!2I83TAZ`+I.NA%86$].74YU(&YA;65S(``@9&%T97,@9
  912. M`"!F;&%G<R``('-I>F5S(``@8V]M;65N=',@`"HJ*B!&:6QE('1Y<&4@;6ES*
  913. M;6%T8V@@*&9I;&4@=G,N(&1I<BD@+2!P<F]G<F%M(&5R<F]R(0`E)4-/35!!^
  914. M4D4E)2`E<R5S("5S)7,*`"HJ*B!-:7-M871C:#H@```M+2TM+2TM+2TM+2TM"
  915. M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T`/3T]/3T]/3T]/3T]/3T]/3T]/3T],
  916. M/3T]/3T]/3T]/3T]/3T]``!.5?_X(&T`""MH``;__&!X(&W__$IH`"AF9B\MX
  917. M``PO+?_\3KH!"E!/*T#_^$JM__AF'B!M__PO*``,(&T`#"\H``(O+(`J3KH6I
  918. M'D_O``Q@#B\M__@O+?_\3KK]"E!/+RT`""\M__Q.N@$*4$]*K?_X9PXO+0`,F
  919. M+RW_^$ZZ`/903R!M__PK4/_\2JW__&:"(&T`#"MH``;_^&`\(&W_^$IH`"AF1
  920. M*B!M__@O*``,(&T`""\H``(O+(`J3KH5K$_O``PO+0`,+RW_^$ZZ`*A03R!MN
  921. M__@K4/_X2JW_^&:^<`!.74YU*BHJ($9I;&4@;6ES<VEN9SH@)R5S)7,G"@!.!
  922. M5?_\+RT`"$ZZ#`A83U)`/P!.N@%L5$\K0/_\+RT`""\M__Q.N@O<4$\@+?_\7
  923. M3EU.=4Y5__P@;0`,*V@`!O_\8"H@;?_\+R@`#"!M``@O*``,3KH"2E!/2D!F)
  924. M""`M__Q.74YU(&W__"M0__Q*K?_\9M!P`&#J3E4``"!M``A*J``$9PX@;0`(6
  925. M(FT`""QI``0LD"!M``A*D&<0(&T`"")M``@L42UH``0`!"!M``PB:``&L^T`A
  926. M"&8,(&T`"")M``PC4``&(&T`#")H``JS[0`(9@X@;0`((FT`#"-H``0`"B!M"
  927. M``@O*``,3KH<+%A/(&T`""\H`!1.NAP>6$\O+0`(3KH<%%A/3EU.=4Y5__PO)
  928. M+0`(3KH*_EA//P`O+0`,3KH*\EA/,A_20%1!/P%A4E1/*T#__"\M``@O+?_\M
  929. M3KH*Q%!/+RT`#"\M__Q.NA'H4$\O+?_\3KH*O%A/4T`@;?_\##``.@``9PY(N
  930. M>@`4+RW__$ZZ$<)03R`M__Q.74YU+P!.5?_\/SP``3\M``A.N@J66$\K0/_\'
  931. M2JW__&80/RT`"$AZ`"Q.NA/$7$]A2G``,"T`"-&L@ZH@+(.JL*R#IF\&*6R#)
  932. MJH.F("W__$Y=3G5$:69F1&ER.B!F86EL960@=&\@86QL;V-A=&4@)6QD(&)Y8
  933. M=&5S(0H`3E4``$JL@X9G#DAX`00O+(.&3KH?.%!/2'H`&$ZZ#6Q83V$F/SP`Y
  934. M`4ZZ')943TY=3G5$:69F1&ER.B!A8FYO<FUA;"!E>&ET(0!.50``+RR#IB\L'
  935. M@[8O+(.R2'H`#DZZ$Q1/[P`03EU.=41I9F9$:7(Z($9I;&5S.B`E;&0[(&1I@
  936. M<F5C=&]R:65S.B`E;&0[(&UA>"!M96UO<GDZ("5L9"!B>71E<PH`3E7_^DCGD
  937. M`#`D;0`()FT`#"!*4HH0$$B`/P!.N@G*5$\[0/_^($M2BQ`02(`_`$ZZ";945
  938. M3SM`__PP+?_^D&W__#M`__IF#$IM__YG!DIM__QFOC`M__I,WPP`3EU.=4Y52
  939. M``!(>@`03KH,>EA/3KK^ZDY=3G55<V%G93H@1&EF9D1I<B!;+6-=(%LM<R!SB
  940. M8W)I<'1F:6QE72!D:7)N86UE,2!D:7)N86UE,@!.5?_8(&T`"$AH`!A(>@#&A
  941. M2&W_V$ZZ!))/[P`,2&W_V$ZZ$@!83R!M``@K:``0__Q";?_Z*WP````!__9@9
  942. M2B`M__S`K?_V9QHP+?_Z0>R`+G((DFW_^D/M_]@3L```$`!@&#`M__I![(`WD
  943. M<@B2;?_Z0^W_V!.P```0`%)M__H@+?_VXX`K0/_V#&T`"?_Z;:Y"+?_A(&T`[
  944. M""\H``P@;0`(+R@`)$AM_]A(>@!)3KH1=$_O`!`@;0`(2J@`%&<2(&T`""\HM
  945. M`!1(>@`W3KH15E!/3EU.=24P,FTM)3`R9"TE,#)Y("4P,F@Z)3`R;CHE,#)ST
  946. M(``E<R`E.&QD("5S"@`Z("5S"@``2F%N`$9E8@!-87(`07!R`$UA>0!*=6X``
  947. M2G5L`$%U9P!397``3V-T`$YO=@!$96,`4W5N9&%Y`$UO;F1A>0!4=65S9&%Y0
  948. M`%=E9&YE<V1A>0!4:'5R<V1A>0!&<FED87D`4V%T=7)D87D``$Y5_]P@;0`(W
  949. M(!#0O``+!A$K0/_\(CP``CJQ("W__$ZZ%%@B/````9!.N@Z>*T#_Z"(\``(Z:
  950. ML2`M__Q.NA1D*T#__"M`__0K0/_X(CP``(ZL("W__$ZZ%")R9$ZZ#FS1K?_HZ
  951. M(CP``(ZL("W_]$ZZ%#(K0/_T*T#_\"(\```%M2`M__!.NA/TY8#1K?_H(CP`I
  952. M``6U("W_]$ZZ%`@K0/_T*T#_["(\```!;2`M_^Q.NA/*T:W_Z"(\```!;2`MY
  953. M__1.NA/@*T#_]%*`*T#_Y'(>("W_Y$ZZ$\PK0/_@<AX@+?_D3KH3EBM`_^0,H
  954. MK0```#O_[&T*#*T````[__AM'@RM````.__L;!@,K0```#O_\&P*#*T````[P
  955. M__AL!%*M__0@+?_D4H#C@$'L@*0R,`@`2,$@+?_TL(%O!%*M_^0@+?_DXX!!<
  956. M[("D,C`(`$C!("W_])"!*T#_X"!M``PPK?_J(&T`##%M_^8``B!M``PQ;?_BS
  957. M``0@;0`((!!R!TZZ$R0@;0`,,4``#"!M``@K:``$_]QR/"`M_]Q.NA+@(&T`[
  958. M##%```9R/"`M_]Q.NA+V(&T`##%```@@;0`(("@`"'(R3KH2N"!M``PQ0``*J
  959. M<`!.74YU3E7_YB!M``@P$$C`*T#_Z+"\```'NFP:(&T`#$*H``@@;0`,0J@`6
  960. M!"!M``Q"D$Y=3G5R!"`M_^A.NA*69P1P`&`"<`%G!CE\`!V`2B!M``@P*``$?
  961. M4T!(P"M`_^P@;0`(,"@``E-`2,`K0/_P("W_Z)"\```'NBM`__@@+?_X4H!R`
  962. M!$ZZ$B0K0/_T(CP```%M("W_^$ZZ#&+0K?_T*T#__$)M_^9@&C`M_^;!_``&/
  963. M0>R`1#(P"`!(P=.M__Q2;?_F,"W_YDC`L*W_\&W:("W_[-&M__PY?``<@$H@,
  964. M;0`,(*W__"!M``@P*``&P?P`/"!M``C0:``(2,`@;0`,(4``!"!M``@P*``*_
  965. MP?P`,DC`(&T`#"%```A@`/\63E7_^D)M__YP`#`M__[E@"!M``AR`#(M__[EM
  966. M@2)M``PD,`@`E+$8`"M"__IG$$JM__IL!G#_3EU.=7`!8/A2;?_^#&T``__^O
  967. M9;YP`&#H3E7^X$AM__(O+0`03KK\NE!/(&T`"$(0*VT`"/_H*VT`#/_L(&W_4
  968. M[%*M_^P;4/_Q9P`";@PM`"7_\68``E1";?[D,"W^Y%)M_N1R`#(`0>W^YA&MW
  969. M__$8`"!M_^Q2K?_L$!`;0/_Q2(!20$'L@38(,``"``!F"`PM`"W_\688,"W^J
  970. MY%)M_N1R`#(`0>W^YA&M__$8`&#"$"W_\4B`2,!@``&2,"W_\DC`@?P`9$A`2
  971. M.T#_YC`M_N12;?[D<@`R`$'M_N81O`!D&`!P`#`M_N1![?[F0C`(`#\M_^9(3
  972. M;?[F+RW_Z$ZZ#$I/[P`*+RT`"$ZZ`S)83TC`T*T`""M`_^A@``&..VW_\O_F.
  973. M8*@[;?_T_^9@H#`M__130,'\``9![(!`*W`(`/[@,"W^Y%)M_N1R`#(`0>W^,
  974. MYA&\`',8`'``,"W^Y$'M_N9",`@`+RW^X$AM_N8O+?_H3KH+UD_O``PO+0`(F
  975. M3KH"OEA/2,#0K0`(*T#_Z&```1H[;?_V_^9@`/\T(&T`$"`0<@=.N@_>Y8!!_
  976. M[("(*W`(`/[@8(P[;?_X_^9@`/\0.VW_^/_F#&T`#/_F908$;0`,_^9@`/[XE
  977. M#&T`#/_X;0I!^@#D*TC^X&`(0?H`W2M(_N!@`/]..VW_^O_F8`#^T#MM__S_W
  978. MYF``_L8P+?[D4FW^Y'(`,@!![?[F$:W_\1@`<``P+?[D0>W^YD(P"`!(;?[FZ
  979. M+RW_Z$ZZ"2A03R\M_^A.N@'\6$](P-&M_^A@7)"\````1&<`_T99@&<`_V13E
  980. M@&<`_W99@&<`_L93@&>(6X!GCEV`9P#^J)"\````"V<`_Q)9@&<`_S!3@&<`4
  981. M_TQ9@&<`_I13@&<`_UY;@&<`_V)=@&<`_AI@`/]B8`P@;?_H4JW_Z!"M__%@'
  982. M`/V&(&W_Z$(03EU.=5!-`$%-`$Y5_^`O!"MM``C_Z$)M__I";?_\,"W__$C`0
  983. MXX!![?_B0G`(`%)M__P,;0`#__QMY$)M__X@;?_H4JW_Z!@09P``F+@\`"!O>
  984. M``"0$`1(@%)`0>R!-@@P``(``&=D0FW_X!`$2(`R+?_@P_P`"M!!D'P`,#M`@
  985. M_^`@;?_H4JW_Z!@0$`1(@%)`0>R!-@@P``(``&;.#&T``__^9@AP`2@?3EU.Y
  986. M=3`M__Y2;?_^2,#C@$'M_^(QK?_@"`"X/``@;QI@%!`$2(`_`$AZ`)1.N@)L"
  987. M7$]*@&?&8`#_7DIM__IG%#MM_^+_\CMM_^3_]#MM_^;_]F!(2FW__F<(#&T`(
  988. M`__^9IH[;?_B_^X[;?_D__`[;?_F_^Q*;?_L9@X[?`>Z_^P[?``!__!@%`QMU
  989. M`$[_[&P&!FT`9/_L!FT';/_L4FW_^@QM``+_^FT`_LHO+0`,2&W_[$ZZ^I90/
  990. M3W``8`#_0B\Z+2X``"!O``0@"")O``@0V6;\3G4@;P`$(`A*&&;\D<`@"%.`1
  991. M3G5.50``2.<((#@M``C([0`*/P1.NA#*)$!*@%1/9PQ"9S\$+PI.N@`.4$\@,
  992. M"DS?!!!.74YU(&\`!$RO``,`"&`"$,%1R/_\3G5P`!`O``6P/`!@8PJP/`!ZM
  993. M8@20/``@3G5P`!`O``6P/`!`8PJP/`!:8@30/``@3G5.50``+PI.N@]6)$!*T
  994. M@&8(<``D7TY=3G4O"B\M``PO+0`(809/[P`,8.A.50``2.<(("\M`!!.N@W`(
  995. M0>R`P"1(6$]*$F80.7P`!8.Z<`!,WP003EU.=2!*(FT`#!`8L!EF!$H`9O:0\
  996. M(4B`9P1<BF#2/RH`!"\M``A.N@$D.`"P?/__7$]F!'``8,0@;0`0$40`#2!M!
  997. M`!`1?``!``P@+0`08*Q.50``2FR#NFT*,"R#NK!L@2)O!G#_3EU.=4JM``AGP
  998. M%"\M``A(>@`R2&R!Y$ZZ`#1/[P`,,"R#NDC`Y8!![(#V+S`(`$AZ`!=(;('D-
  999. M3KH`%'``3^\`#&"^)7,Z(``E<PH``$Y5```I;0`(@WI(;0`0+RT`#$AZ``Y.1
  1000. MN@A<3^\`#$Y=3G5.50``+RR#>C\M``A.N@P>7$].74YU(&\`!#`O``@2&&<*E
  1001. ML@!F^"`(4X!.=7``3G4P/'__8`0P+P`,4T!K%"!O``0B;P`(L0EF#%-(2AA7X
  1002. MR/_V<`!.=6,$<`%.=7#_3G5.50``/RT`##\\`P$O+0`(80903TY=3G5.50``2
  1003. M2.</,"1M``A.NA`4)FR#O'@`8`XP!,'\``9*LP@`9PY21+AL@W!M['H&8```!
  1004. MQ`@M``$`#&<P2'C__R\*3KH2*"P`4$]G("\&3KH24B\*3KH1QDJ`4$]F#DZZ&
  1005. M$?0Z`+!\`,UF``",2'@#[2\*3KH2!BP`2H903V9@""T````,9@1Z`6!L2'@#)
  1006. M[B\*3KH1Z"P`4$]F"$ZZ$;@Z`&!42'@`(4AZ`)).NA)^+@!03V<*+P=.NA(@;
  1007. M6$]@'DAX``%(>@""+P9.NA'D2'C__T*G+P9.NA&Z3^\`&&`F,"T`#,!\!0"P!
  1008. M?`4`9A@O!DZZ$11Z!%A/.46#NG#_3-\,\$Y=3G4P!,'\``8GA@@`,`3!_``&H
  1009. M($#1RS%M``P`!`@M``,`#&<02'@``4*G+P9.NA%@3^\`##`$8,)D;W,N;&EB(
  1010. M<F%R>0```$Y5```O"B1M``A*$F<@($I2BA`02(`_`$ZZ"CZP?/__5$]F"'#_]
  1011. M)%].74YU8-P_/``*3KH*)%1/8.QA<$/L@WI%[(-ZM<EF#C(\`")K"'0`(L)1@
  1012. MR?_\*4^#P"QX``0I3H/$2.>`@`@N``0!*6<02_H`"$ZN_^)@!D*G\U].<T/ZX
  1013. M`"!.KOYH*4"#R&8,+CP``X`'3J[_E&`$3KH`&E!/3G5D;W,N;&EB<F%R>0!)%
  1014. M^0``?_Y.=4Y5```O"DAY``$``#`L@W#!_``&+P!.NA#4*4"#O%!/9A1"ITAYO
  1015. M``$``$ZZ$)103RYL@\!.=2!L@[Q":``$(&R#O#%\``$`$"!L@[PQ?``!``H@1
  1016. M;(/`("R#P)"H``10@"E`@\P@;(/,(+Q-04Y80J=.NA"()$!*J@"L6$]G+B\M2
  1017. M``PO+0`(+PI.N@"N.7P``8/0(&R#O`!H@```!"!L@[P`:(````I/[P`,8$)(0
  1018. M:@!<3KH0IDAJ`%Q.NA!H*4"#TB!L@])*J``D4$]G$"!L@](B:``D+Q%.N@\\_
  1019. M6$\O+(/2+PI.N@-2*6R#TH/64$].N@]<(&R#O""`3KH/DB!L@[PA0``&9Q9(-
  1020. M>`/M2'H`*DZZ#VX@;(.\(4``#%!/+RR#UC\L@]I.NN,,0F=.N@U64$\D7TY=$
  1021. M3G4J`$Y5``!(YPPP)&T`$"!M``A*J`"L9Q@@;0`(("@`K.6`*``@1"`H`!#E%
  1022. M@"9`8`0F;(-R$!-(@$C`T*T`#%2`.4"#W$*G,"R#W$C`+P!.N@]F*4"#WE!/Z
  1023. M9@A,WPPP3EU.=1`32(`Z`#\%($M2B"\(+RR#WDZZ`7XP!4C`($#1[(/>0_H!D
  1024. M1!#99OP_+0`.+PHO+(/>3KH!.B!L@]Y",%``.7P``8/:,`5(P-"L@]XF0%*+@
  1025. M)$M/[P`4$!-(@#H`L'P`(&<8NGP`"6<2NGP`#&<,NGP`#6<&NGP`"F8$4HM@U
  1026. MV`P3`"!M>@P3`")F+E*+($M2BQ`02(`Z`&<>($I2BA"%NGP`(F80#!,`(F8$S
  1027. M4HM@!D(J__]@`F#68#@@2U*+$!!(@#H`9R:Z?``@9R"Z?``)9QJZ?``,9Q2ZH
  1028. M?``-9PZZ?``*9P@@2E**$(5@SB!*4HI"$$I%9@)3BU)L@]I@`/]:0A)"IS`L0
  1029. M@]I20$C`Y8`O`$ZZ#D0I0(/64$]F"$)L@]I@`/[8>@`F;(/>8"0P!4C`Y8`@H
  1030. M;(/6(8L(`"!+(`A*&&;\D<!3B#`(4D!(P-?`4D6Z;(/:;=8P!4C`Y8`@;(/6)
  1031. M0K`(`&``_I0@`#`\?_]@!#`O``P@;P`$2AAF_%-((F\`"%-`$-E7R/_\9P)"1
  1032. M$"`O``1.=4SO`P``!"`(,B\`#&`"$-E7R?_\9P9206`"0AA1R?_\3G5(YW``6
  1033. M-`'$P"8!2$/&P$A#0D/4@TA`P,%(0$)`T(),WP`.3G5.;R!E<G)O<@!&:6QE`
  1034. M(&YO="!F;W5N9`!"860@9FEL92!H86YD;&4`26YS=69F:6-I96YT(&UE;6]RG
  1035. M>0!&:6QE(&5X:7-T<P!);G9A;&ED(&9U;F-T:6]N(&YU;6)E<@!4;V\@;6%N]
  1036. M>2!O<&5N(&9I;&5S`$YO="!A(&-O;G-O;&4@9&5V:6-E`$EN=F%L:60@86-CA
  1037. M97-S(&-O9&4`4F5S=6QT('1O;R!L87)G90!!<F=U;65N="!O=70@;V8@9&]MR
  1038. M86EN``!.50``2.<.,"1M``A"ITAZ`(Y.N@SD*4"#XE!/9@A,WPQP3EU.=2!M/
  1039. M``PB:``D+RD`!$ZZ#10H`%A/9U)(>@!M($0O*``V3KH,YB9`2H!03V<T2'@#.
  1040. M[2\+3KH+[BP`4$]G)"`&Y8`J`"!%)6@`"`"D)48`G$AX`^U(>@`X3KH+RB5`$
  1041. M`*!03R\$3KH,LEA/+RR#XDZZ#`Y"K(/B6$]@@&EC;VXN;&EB<F%R>0!724Y$9
  1042. M3U<`*@!.50``+P0I;0`(@WY(;0`0+RT`#$AZ`!I.N@#<.``@;(-^0A`P!$_O[
  1043. M``PH'TY=3G5.50``(&R#?E*L@WX0+0`)$(!(@,!\`/].74YU3E4``$AM``PO*
  1044. M+0`(2'H$8$ZZ`)A/[P`,3EU.=4Y5``!(YP@@)&T`#@QM``0`$F8((&T`""@06
  1045. M8!Q*;0`,;PP@;0`(<``P$"@`8`H@;0`(,!!(P"@`0FT`$DIM``QL$$1M``Q*=
  1046. MA&P(1(0[?``!`!(R+0`,2,$@!$ZZ`Y!![($D4XH4L```,BT`#$C!(`1.N@.&9
  1047. M*`!FVDIM`!)G!E.*%+P`+2`*3-\$$$Y=3G5.5?\B2.<(,"1M``@F;0`,0FW_@
  1048. M^BMM`!#__"!+4HL0$$B`.`!G``+NN'P`)68``LQ"+?\P.WP``?_X.WP`(/_V"
  1049. M.WPG$/_T($M2BQ`02(`X`+!\`"UF#D)M__@@2U*+$!!(@#@`N'P`,&80.WP`U
  1050. M,/_V($M2BQ`02(`X`+A\`"IF&"!M__Q4K?_\.U#_\B!+4HL0$$B`.`!@,D)M2
  1051. M__)@'#`M__+!_``*T$20?``P.T#_\B!+4HL0$$B`.``P!%)`0>R!-@@P``(`*
  1052. M`&;4N'P`+F9:($M2BQ`02(`X`+!\`"IF&"!M__Q4K?_\.U#_]"!+4HL0$$B`*
  1053. M.`!@,D)M__1@'#`M__3!_``*T$20?``P.T#_]"!+4HL0$$B`.``P!%)`0>R!9
  1054. M-@@P``(``&;4.WP``O_PN'P`;&82($M2BQ`02(`X`#M\``3_\&`0N'P`:&8*X
  1055. M($M2BQ`02(`X`#`$2,!@>CM\``C_[F`6.WP`"O_N8`X[?``0_^Y@!CM\__;_!
  1056. M[C\M__!(;?\P/RW_[B\M__Q.NOWD*T#_ZC`M__!(P-&M__Q/[P`,8%P@;?_\U
  1057. M6*W__")0*TG_ZB`)2AEF_)/`4XD[2?_P8$H@;?_\5*W__#@00>W_+RM(_^H0=
  1058. MA&`HD+P```!C9^)3@&>2D+P````+9P#_<EF`9[)5@&<`_W!7@&<`_W)@S$'ME
  1059. M_S"1[?_J.TC_\#`M__"P;?_T;P8[;?_T__!*;?_X9V@@;?_J#!``+6<*(&W_<
  1060. MZ@P0`"MF+@QM`##_]F8F4VW_\B!M_^I2K?_J$!!(@#\`3I*P?/__5$]F"G#_Q
  1061. M3-\,$$Y=3G5@%C\M__9.DK!\__]43V8$</]@Y%)M__HP+?_R4VW_\K!M__!N'
  1062. MW$)M_^Y@("!M_^I2K?_J$!!(@#\`3I*P?/__5$]F!'#_8+!2;?_N(&W_ZDH0/
  1063. M9PHP+?_NL&W_]&W.,"W_[M%M__I*;?_X9BA@&#\\`"!.DK!\__]43V8&</]@^
  1064. M`/]X4FW_^C`M__)3;?_RL&W_\&[:8!8_!$Z2L'S__U1/9@9P_V``_U)2;?_ZL
  1065. M8`#]"#`M__I@`/]"2.=(`$*$2H!J!$2`4D1*@6H&1($*1``!83Y*1&<"1(!,@
  1066. MWP`22H!.=4CG2`!"A$J`:@1$@%)$2H%J`D2!81H@`6#8+P%A$B`!(A]*@$YUO
  1067. M+P%A!B(?2H!.=4CG,`!(04I!9B!(038!-`!"0$A`@,,B`$A`,@*"PS`!0D%(^
  1068. M04S?``Q.=4A!)@$B`$)!2$%(0$)`=`_0@-.!MH%B!)*#4D!1RO_R3-\`#$YU4
  1069. M3E4``$AL@<X_+0`(3KH`"%Q/3EU.=4Y5```O!#@M``@O+0`*/P1.N@`PN'P`;
  1070. M"EQ/9B0@;0`*$"@`#$B`"```!V<4/SS__R\M``I.N@#T7$\H'TY=3G5@^$Y5B
  1071. M```O"B1M``H@4K'J``1E&#`M``C`?`#_/P`O"DZZ`,A<3R1?3EU.=2!24I(0,
  1072. M+0`)$(!(@,!\`/]@Z$Y5```O"D'L@;@D2"!*U?P````6+PAA$%A/0>R#<+7(\
  1073. M9>HD7TY=3G5.50``2.<(("1M``AX`"`*9@IP_TS?!!!.74YU2BH`#&=0""H`J
  1074. M`@`,9PP_//__+PIA4C@`7$\0*@`-2(`_`$ZZ!1R(0`@J``$`#%1/9PHO*@`(\
  1075. M3KH"+EA/""H`!0`,9Q(O*@`23KH"P"\J`!).N@(44$]"DD*J``1"J@`(0BH`<
  1076. M##`$8)!.5?_^2.<(("1M``A!^O]&*4B#Y@@J``0`#&<*</],WP003EU.=0@J;
  1077. M``(`#&<P(%*1Z@`(.`@_!"\J``@0*@`-2(`_`$ZZ`H"P1%!/9Q`(Z@`$``Q"D
  1078. MDD*J``1P_V#`#&W__P`,9A`(J@`"``Q"DD*J``1P`&"H2JH`"&8(+PI.N@":+
  1079. M6$\,:@`!`!!F*AMM``W__S\\``%(;?__$"H`#4B`/P!.N@(BL'P``5!/9J`P\
  1080. M+0`,8`#_:B2J``@P*@`02,#0J@`()4``!`CJ``(`#"!24I(0+0`-$(!(@,!\I
  1081. M`/]@`/\^3E4``"\*0>R!N"1(2BH`#&<8U?P````60>R#<+7(90AP`"1?3EU.F
  1082. M=6#B0I)"J@`$0JH`""`*8.I.5?_\+PHD;0`(/SP$`$ZZ`,`K0/_\5$]F\2
  1083. M``$`$"!*T?P````.)4@`""1?3EU.=35\!```$`CJ``$`#"5M__P`"!`J``U(D
  1084. M@#\`3KH`XDI`5$]G!@`J`(``#&#.3E4``$CG`#`D;(."8!0F4B`J``10@"\`W
  1085. M+PI.N@2:4$\D2R`*9NA"K(."3-\,`$Y=3G5.50``+PI!^O_&*4B#ZD*G("T`M
  1086. M"%"`+P!.N@1$)$!*@%!/9@AP`"1?3EU.=22L@X(E;0`(``0I2H."(`I0@&#F>
  1087. M3E4``'``,"T`""\`8;)83TY=3G5.50``2.<`,)?+)&R#@F`.(&T`"%&(L<IG&
  1088. M$B9*)%(@"F;N</],WPP`3EU.=2`+9P0FDF`$*5*#@B`J``10@"\`+PI.N@/LY
  1089. M<`!03V#83E4``"\*,"T`",'\``8D0-7L@[Q*;0`(;0XP+0`(L&R#<&P$2I)F/
  1090. M#CE\``*#NG#_)%].74YU,"T`",'\``8@;(.\+S`(`$ZZ`NI*@%A/9P1P`6`"?
  1091. M<`!@V$Y5```O+0`(3KH"D$J`6$]F#DZZ`KXY0(.Z</].74YU<`!@^$Y5``!(\
  1092. MYPP@."T`"$ZZ`'`P!,'\``8D0-7L@[Q*1&T*N&R#<&P$2I)F$#E\``*#NG#_4
  1093. M3-\$,$Y=3G4P*@`$P'P``V8*.7P`!8.Z</]@Y'``,"T`#B\`+RT`"B\23KH"_
  1094. MJBH`L+S_____3^\`#&8,3KH"/CE`@[IP_V"X(`5@M$Y5__Q(>!``0J=.N@,6N
  1095. M*T#__`@```Q03V<22FR#T&8(("W__$Y=3G5.N@`&<`!@]$Y5``!(>``$2'H`Z
  1096. M'$ZZ`B8O`$ZZ`D8_/``!3KH`#D_O``Y.74YU7D,*`$Y5``!*K(/F9P8@;(/FF
  1097. M3I`_+0`(3KH`"%1/3EU.=4Y5__PO!#`M``A(P"M`__Q*K(.\9RAX`&`*/P1..
  1098. MN@#^5$]21+AL@W!M\#`L@W#!_``&+P`O+(.\3KH".%!/2JR#ZF<&(&R#ZDZ0(
  1099. M2JR#=F<*+RR#=DZZ`:Q83TJL@^YG""!L@^X@K(/R2JR#]F<*+RR#]DZZ`<A8!
  1100. M3TJL@_IG"B\L@_I.N@&X6$]*K(/^9PHO+(/^3KH!J%A/2JR$`F<*+RR$`DZZE
  1101. M`9A83RQX``0(+@`$`2EG%"\-2_H`"DZN_^(J7V`&0J?S7TYS2JR#TF8P2JR#T
  1102. MWF<H,"R#W$C`+P`O+(/>3KH!D#`L@]I20$C`Y8`O`"\L@]9.N@%\3^\`$&`.0
  1103. M3KH!9B\L@]).N@&66$\@+?_\+FR#P$YU*!].74YU3E4``$CG#B`X+0`(,`3!D
  1104. M_``&)$#5[(.\2D1M"KAL@W!L!$J29A`Y?``"@[IP_TS?!'!.74YU""H`!P`$Q
  1105. M9@@O$DZZ``I83T*2<`!@XB(O``0L;(/(3N[_W"(O``0L;(/(3N[_@B(O``0LS
  1106. M;(/(3N[_N$[Z``),[P`&``0L;(/(3N[_FDSO``8`!"QL@\A.[O^4+&R#R$[N'
  1107. M_\I.^@`"+&R#R$[N_WPB+P`$+&R#R$[N_RA.^@`"3.\`!@`$+&R#R$[N_ZQ,M
  1108. M[P`&``0L;(/(3N[_XBQL@\A.[O_$3.\`#@`$+&R#R$[N_[Y.^@`"(B\`!"QL[
  1109. M@\A.[O^F3.\`#@`$+&R#R$[N_]!(YP$$3.\@@``,+&R#Q$ZN_Y1,WR"`3G5.,
  1110. M^@`"(F\`!"QL@\1.[OYB3OH``DSO``,`!"QL@\1.[O\Z(F\`!"QL@\1.[O[:T
  1111. M+&R#Q$[N_WQ.^@`"(F\`!"`O``@L;(/$3N[_+B!O``0L;(/$3N[^C"QL@\0B-
  1112. M;P`$("\`"$[N_=@B;P`$+&R#Q$[N_H9,[P`#``0L;(/$3N[^SB!O``0L;(/$J
  1113. M3N[^@$SO`P``!"QL@^).[O^@(&\`!"QL@^).[O^F(&\`!"QL@^).[O^R```#%
  1114. M[`````$````!```;S`````````/P`````E]?2#!?;W)G``````````)?;6%I_
  1115. M;@````````0````"7T%D9$YO9&4```&D````!%]#;VQL96-T1FEL97,`````C
  1116. M``(`````!%]#;VUP87)E3&ES=',```````1J`````U]#;VUP87)E1&ER<P``D
  1117. M!?8````#7T-O;7!A<F5&:6QE```(!@````1?0V]M<&%R949I;&5S```````*9
  1118. MH@````-?1'5P4W1R:6YG``````N>`````U]&:6YD1FEL90``````"]`````#.
  1119. M7T9R965.;V1E```````,%`````-?36%K941I<DYA;64```RH`````E]->4%L/
  1120. M;&]C```-'@````)?37E%>&ET````#90````#7U)E<&]R=%-T871S```-W@``C
  1121. M``)?<W1R:6-M<```#CP````"7U5S86=E``````Z:````!%]7<FET949I;&5)B
  1122. M;F9O``````[F`````U]$4U1O1&%T90``````$%8````#7T1A=&54;T13````1
  1123. M```2)@````-?0V]M<&%R9413`````!-``````E]$4U1O4W1R```3C@````)?!
  1124. M4W1R5&]$4P``%CX````"7W-T<F-P>0```!>@`````E]S=')L96X````7L```D
  1125. M``)?8V%L;&]C````%\(````"7W-E=&UE;0```!?V`````E]T;W5P<&5R```8I
  1126. M"@````)?=&]L;W=E<@``&"(````"7V9O<&5N`````!@Z`````E]F<F5O<&5N,
  1127. M```89`````)?<&5R<F]R````&-P````"7V9P<FEN=&8``!D^`````E]I;F1E=
  1128. M>``````9=@````)?<W1R8VUP````&9`````"7W-T<FYC;7```!F6`````E]C%
  1129. M<F5A=``````9P`````)?;W!E;@``````&=@````"7W!U=',``````!L>````O
  1130. M`BYB96=I;@`````;6`````)?9V5T830`````&\H````"7U]M86EN`````!O25
  1131. M`````U]?8VQI7W!A<G-E````'0H````"7W-T<F-A=````![2`````E]S=')ND
  1132. M8V%T```>V`````)?<W1R;F-P>0``'OP````"+FUU;'4``````!\>`````U]?M
  1133. M=V)?<&%R<V4`````(`@````"7W-P<FEN=&8``""\`````E]P<FEN=&8````A3
  1134. M"`````)?9F]R;6%T````(;(````"+F1I=G,``````"3.`````BYM;V1S`````
  1135. M```D]@````(N;6]D=0``````)1`````"+F1I=G4``````"4<`````E]P=71CZ
  1136. M:&%R```E=@````)?87!U=&,`````)8P````"7W!U=&,``````"7.`````E]F<
  1137. M8VQO<V4````F,@````)?9FQS:%\`````)K8````#7VYE=W-T<F5A;0`````G6
  1138. MF`````)?9V5T8G5F9@``)]`````"7VQM86QL;V,``"A@`````E]M86QL;V,`Y
  1139. M```HH`````)?9G)E90``````*+0````"7VES871T>0```"D``````E]U;FQIH
  1140. M;FL````I6`````)?=W)I=&4`````*7P````#7T-H:U]!8F]R=``````I^@``]
  1141. M``)?7V%B;W)T````*BH````"7V5X:70``````"I4`````E]?97AI=``````JI
  1142. M<@````)?8VQO<V4`````*XX````"7U]#;&]S90```"O:`````U]?0W5R<F5N-
  1143. M=$1I<@``*^8````#7U]$96QE=&5&:6QE```K\@````)?17AA;6EN90``*_X`F
  1144. M```#7U]%>&%M:6YE```````L`@````)?17A.97AT````+!`````"7U]);G!U+
  1145. M=````"P>`````E]);T5R<@`````L)@````)?7TEO17)R````+"H````$7U])4
  1146. M<TEN=&5R86-T:79E````+#(````"7TQO8VL``````"P^`````E]?3&]C:P``L
  1147. M```L0@````)?7T]P96X`````+%`````"7U]/=71P=70``"Q>`````E]?4V5ET
  1148. M:P`````L9@````)?56Y,;V-K````+'0````"7U]5;DQO8VL``"QX`````E]?Z
  1149. M5W)I=&4````LA`````)?06QE<G0`````+)(````$7T-L;W-E3&EB<F%R>0```
  1150. M````+*H````$7U]#;&]S94QI8G)A<GD`````+*X````#7T%L;&]C365M````I
  1151. M```LN@````-?7T%L;&]C365M`````"R^`````U]?1FEN9%1A<VL`````+,P`H
  1152. M```"7U]&;W)B:60``"S8`````E]&<F5E365M```LX`````-?7T9R965-96T`*
  1153. M`````"SD`````E]?1V5T37-G```L]`````1?7T]P96Y,:6)R87)Y```````M-
  1154. M``````-?7U)E<&QY37-G`````"T0`````U]?4V5T4VEG;F%L````+1P````#(
  1155. M7U]786ET4&]R=``````M*@````1?1FEN9%1O;VQ4>7!E```````M-@````1?Y
  1156. M1G)E941I<VM/8FIE8W0````M1`````1?1V5T1&ES:T]B:F5C=``````M4```W
  1157. M``)?7T@P7V5N9```+5P````````#\@```^H```#>``````````````6,```%M
  1158. MG```![0```G.```)U@``"=X```GF```)[@``"X0M+2TM87!S/S]D97=R+2TM8
  1159. M+2T```_L`!\```_P`!P```_T`!\```_X`!X```_\`!\``!```!X``!`$`!\`A
  1160. M`!`(`!\``!`,`!X``!`0`!\``!`4`!X``!`8`!\``!`<```0(P``$"H``!`R4
  1161. M```0/```$$4``!!,_____P`>`#H`60!W`)8`M`#3`/(!$`$O`4T!;'(`````>
  1162. M`'(K`````G<````#`7<K```#`F$````)`6$K```)`G@````%`7@K```%`@``K
  1163. M````````'T(``!]+```?6@``'VH``!]^```?B@``'Z(``!^V```?RP``']\`1
  1164. M`!_P``LP,3(S-#4V-S@Y86)C9&5F````("`@("`@("`@,#`P,#`@("`@("`@L
  1165. M("`@("`@("`@(""00$!`0$!`0$!`0$!`0$!`#`P,#`P,#`P,#$!`0$!`0$`)Q
  1166. M"0D)"0D!`0$!`0$!`0$!`0$!`0$!`0$!`4!`0$!`0`H*"@H*"@("`@("`@("-
  1167. M`@("`@("`@("`@("0$!`0"```````````````````0`````!````````````Z
  1168. M``````````$!`````0`````````````````````!`@````$`````````````'
  1169. M`````````````````````````````````````````````````````````````
  1170. M`````````````````````````````````````````````````````````````
  1171. M`````````````````````````````````````````````````````````````
  1172. M`````````````````````````````````````````````````````````````
  1173. M`````````````````````````````````````````````````````````````
  1174. M`````````````````````````````````````````````````````````````
  1175. M`````````````````````````````````````````````````````````````
  1176. M`````````````````````````````````````````````````````````````
  1177. M````````````%``````````````#[````"<`````````"`````P````0````.
  1178. M%````!@````<````(````"0````H````/@```$0```!*````4````%8```!<"
  1179. M````8@```&@```!N````=````'H```"`````A@```(H```".````D@```)8`L
  1180. M``":````G@```/0```#X````_````0````$$```!"````0P```$0```!%```B
  1181. M`1@```$<`````````_`````"7U](,5]O<F<``````````U]I9VYO<F5#87-EK
  1182. M```````````"7VQE=F5L```````"````!%]O=71P=7138W)I<'0````````$(
  1183. M`````E]V97)B;W-E````!@````-?8V%L96YD87(````````^`````U]D87E.E
  1184. M86UE<P```````(8````#7W-Y<U]E<G)L:7-T````]`````-?<WES7VYE<G(`<
  1185. M``````$@`````E]C='!?```````!-`````)?0V)U9F9S`````;8````"7U]N\
  1186. M=6UD978```-N````!%]?9&5T86-H7VYA;64```````-P````!%]?9&5T86-H;
  1187. M7V-U<F1I<@````-T`````E]?2#%?96YD```#>`````)?7T@R7V]R9P```W@`%
  1188. M```!7V9I8@```X0````"7VQI<W0Q``````.(`````E]L:7-T,@`````#E@``9
  1189. M``-?;6%X365M57-E9`````.D`````U]M96U);E5S90```````Z@````#7W-CG
  1190. M<FEP=$9I;&4````#K`````-?=&]T86Q&:6QE<P````.P`````U]T;W1A;$1I-
  1191. M<G,``````[0````"7V5R<FYO``````.X`````E]?9&5V=&%B```#N@````)?R
  1192. M7W-A=G-P`````[X````"7U-Y<T)A<V4```/"`````E]$3U-"87-E```#Q@``X
  1193. M``-?7W-T:V)A<V4```````/*````!%]%;F%B;&5?06)O<G0```````/.````-
  1194. M`U]70F5N8VA-<V<``````]`````"7U]A<F=V``````/4`````E]?87)G8P``W
  1195. M```#V`````-?7V%R9U]L96X```````/:`````U]?87)G7VQI;@```````]P`-
  1196. M```#7TEC;VY"87-E```````#X`````)?8VQS7P```````^0````"7U]C;&X`O
  1197. M``````/H`````U]?=')A<&%D9'(``````^P````#7U]O;&1T<F%P```````#G
  1198. M\`````1?36%T:%1R86YS0F%S90`````#]`````-?36%T:$)A<V4```````/XY
  1199. M````!5]-871H265E941O=6)"87-"87-E```#_`````9?36%T:$EE965$;W5BQ
  1200. M5')A;G-"87-E``````0``````E]?2#)?96YD```$!`````````/R```#ZP``"
  1201. &``$```/RV
  1202. ``
  1203. end
  1204. size 15576
  1205. SHAR_EOF
  1206. cat << \SHAR_EOF > MRDates.c
  1207. /*
  1208.     MRDates - AmigaDOS date support routines.
  1209.     07/03/88
  1210.  
  1211.     This package is a hybrid of code from Thad Floryan, Doug Merrit and
  1212.     myself.  I wanted a reliable set of AmigaDOS date conversion routines
  1213.     and this combination seems to work pretty well.  The star of the show
  1214.     here is Thad's algorithm for converting the "days elapsed" field of
  1215.     an AmigaDOS DateStamp, using an intermediate Julian date format.  I
  1216.     lifted/embellished some of the data structures from Doug's ShowDate
  1217.     package and wrote the DateToDS function.
  1218.  
  1219.     History:    (most recent change first)
  1220.  
  1221.     12/31/88 -MRR-
  1222.         StrToDS was not handling the null string properly.
  1223.  
  1224.     11/01/88 -MRR- Added Unix-style documentation.
  1225.  
  1226.     07/03/88 - Changed some names:
  1227.                   Str2DS => StrToDS
  1228.                   DS2Str => DSToStr
  1229.  */
  1230.  
  1231. #define MRDATES
  1232. #include "MRDates.h"
  1233. #include <exec/types.h>
  1234. #include <ctype.h>
  1235.  
  1236.  
  1237. char *index();
  1238.  
  1239. #define DATE_SEPARATORS     "/:-."
  1240. #define MINS_PER_HOUR       60
  1241. #define SECS_PER_MIN        60
  1242. #define SECS_PER_HOUR       (SECS_PER_MIN * MINS_PER_HOUR)
  1243. #define TICS_PER_SEC        50
  1244.  
  1245. #define YEARS_PER_CENTURY   100
  1246.  
  1247.  
  1248. /*
  1249.    definitions to calculate current date
  1250.  */
  1251. #define FEB             1   /* index of feb. in table (for leap years) */
  1252. #define DAYS_PER_WEEK   7
  1253. #define DAYS_PER_YEAR   365
  1254. #define YEARS_PER_LEAP  4
  1255. #define START_YEAR      1978
  1256. #define FIRST_LEAP_YEAR 1980
  1257. #define LEAP_ADJUST     (FIRST_LEAP_YEAR - START_YEAR)
  1258. #define LEAP_FEB_DAYS   29
  1259. #define NORM_FEB_DAYS   28
  1260. #define IsLeap(N)       (((N) % YEARS_PER_LEAP) ? 0 : 1)
  1261.  
  1262.  
  1263. /*  FUNCTION
  1264.         DSToDate - convert a DateStamp to a DATE.
  1265.  
  1266.     SYNOPSIS
  1267.         int DSToDate(dateStamp, date)
  1268.             struct DateStamp *dateStamp;
  1269.             DATE *date;
  1270.  
  1271.     DESCRIPTION
  1272.       Extracts the date components from an AmigaDOS datestamp.
  1273.       The calculations herein use the following assertions:
  1274.  
  1275.       146097 = number of days in 400 years per 400 * 365.2425 = 146097.00
  1276.        36524 = number of days in 100 years per 100 * 365.2425 =  36524.25
  1277.         1461 = number of days in   4 years per   4 * 365.2425 =   1460.97
  1278.  
  1279.     AUTHOR
  1280.       Thad Floryan, 12-NOV-85
  1281.       Mods by Mark Rinfret, 04-JUL-88
  1282.  
  1283.     SEE ALSO
  1284.         Include file MRDates.h.
  1285.  
  1286.  */
  1287.  
  1288. #define DDELTA 722449   /* days from Jan.1,0000 to Jan.1,1978 */
  1289.  
  1290. static int mthvec[] =
  1291.    {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
  1292.  
  1293. int
  1294. DSToDate(ds, date)
  1295.     long *ds; DATE *date;
  1296.  
  1297. {
  1298.  
  1299.     long jdate, day0, day1, day2, day3;
  1300.     long year, month, day, temp;
  1301.  
  1302.     jdate = ds[0] + DDELTA;      /* adjust internal date to Julian */
  1303.  
  1304.     year = (jdate / 146097) * 400;
  1305.     day0  = day1 = jdate %= 146097;
  1306.     year += (jdate / 36524) * 100;
  1307.     day2  = day1 %= 36524;
  1308.     year += (day2 / 1461) * 4;
  1309.     day3  = day1 %= 1461;
  1310.     year += day3 / 365;
  1311.     month = 1 + (day1 %= 365);
  1312.     day = month % 30;
  1313.     month /= 30;
  1314.  
  1315.     if ( ( day3 >= 59 && day0 < 59 ) ||
  1316.         ( day3 <  59 && (day2 >= 59 || day0 < 59) ) )
  1317.       ++day1;
  1318.  
  1319.     if (day1 > mthvec[1 + month]) ++month;
  1320.     day = day1 - mthvec[month];
  1321.     date->Dyear = year;
  1322.     date->Dmonth = month;
  1323.     date->Dday = day;
  1324.     date->Dweekday = ds[0] % DAYS_PER_WEEK;
  1325.  
  1326.     temp = ds[1];               /* get ds_Minute value */
  1327.     date->Dhour = temp / MINS_PER_HOUR;
  1328.     date->Dminute = temp % MINS_PER_HOUR;
  1329.     date->Dsecond = ds[2] / TICS_PER_SEC;
  1330.     return 0;
  1331. }
  1332.  
  1333. /*  FUNCTION
  1334.         DateToDS(date, dateStamp)
  1335.  
  1336.     SYNOPSIS
  1337.         void DateToDS(date, dateStamp)
  1338.              DATE *date;
  1339.              struct DateStamp *dateStamp;
  1340.  
  1341.     DESCRIPTION
  1342.         DateToDS converts the special DATE format to a DateStamp.
  1343.  */
  1344.  
  1345. DateToDS(date, ds)
  1346.     DATE *date; long *ds;
  1347. {
  1348.     long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear;
  1349.     int month;
  1350.  
  1351.     /* Note the special handling for year < START_YEAR.  In this case,
  1352.      * the other fields are not even checked - the user just gets the
  1353.      * "start of time".
  1354.      */
  1355.     if ((thisYear = date->Dyear) < START_YEAR) {
  1356.         ds[0] = ds[1] = ds[2] = 0;
  1357.         return;
  1358.     }
  1359.     if (IsLeap(thisYear))
  1360.         calendar[FEB].Mdays = LEAP_FEB_DAYS;
  1361.  
  1362.     thisDay = date->Dday - 1;
  1363.     thisMonth = date->Dmonth -1;
  1364.     yearsElapsed = thisYear - START_YEAR;
  1365.     leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP;
  1366.     daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears;
  1367.     for (month = 0; month < thisMonth; ++month)
  1368.         daysElapsed += calendar[month].Mdays;
  1369.     daysElapsed += thisDay;
  1370.     calendar[FEB].Mdays = NORM_FEB_DAYS;
  1371.     ds[0] = daysElapsed;
  1372.     ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute;
  1373.     ds[2] = date->Dsecond * TICS_PER_SEC;
  1374. }
  1375. /*  FUNCTION
  1376.         CompareDS - compare two DateStamp values.
  1377.  
  1378.     SYNOPSIS
  1379.         int CompareDS(date1, date2)
  1380.             struct DateStamp *date1, *date2;
  1381.  
  1382.     DESCRIPTION
  1383.         CompareDS performs an ordered comparison between two DateStamp
  1384.         values, returning the following result codes:
  1385.  
  1386.             -1 => date1 < date2
  1387.              0 => date1 == date2
  1388.              1 => date1 > date2
  1389.  
  1390.     NOTE:
  1391.         This routine makes an assumption about the DateStamp structure,
  1392.         specifically that it can be viewed as an array of 3 long integers
  1393.         in days, minutes and ticks order.
  1394.  */
  1395.  
  1396. int
  1397. CompareDS(d1, d2)
  1398.     long *d1, *d2;
  1399. {
  1400.     USHORT i;
  1401.     long compare;
  1402.  
  1403.     for (i = 0; i < 3; ++i) {
  1404.         if (compare = (d1[i] - d2[i])) {
  1405.             if (compare < 0) return -1;
  1406.             return 1;
  1407.         }
  1408.     }
  1409.     return 0;                       /* dates match */
  1410. }
  1411.  
  1412. /*  FUNCTION
  1413.         DSToStr - convert a DateStamp to a formatted string.
  1414.  
  1415.     SYNOPSIS
  1416.         void DSToStr(str,fmt,d)
  1417.              char *str, *fmt;
  1418.              struct DateStamp *d;
  1419.  
  1420.     DESCRIPTION
  1421.         DSToStr works a little like sprintf.  It converts a DateStamp
  1422.         to an ascii formatted string.  The formatting style is dependent
  1423.         upon the contents of the format string, fmt, passed to this
  1424.         function.
  1425.  
  1426.         The content of the format string is very similar to that
  1427.         for printf, with the exception that the following letters
  1428.         have special significance:
  1429.             y => year minus 1900
  1430.             Y => full year value
  1431.             m => month value as integer
  1432.             M => month name
  1433.             d => day of month (1..31)
  1434.             D => day name ("Monday".."Sunday")
  1435.             h => hour in twenty-four hour notation
  1436.             H => hour in twelve hour notation
  1437.             i => 12 hour indicator for H notation (AM or PM)
  1438.             I => same as i
  1439.             n => minutes    (sorry...conflict with m = months)
  1440.             N => same as n
  1441.             s => seconds
  1442.             S => same as s
  1443.  
  1444.         All other characters are passed through as part of the normal
  1445.         formatting process.  The following are some examples with
  1446.         Saturday, July 18, 1987, 13:53 as an input date:
  1447.  
  1448.             "%y/%m/%d"          => 87/7/18
  1449.             "%02m/%02d/%2y"     => 07/18/87
  1450.             "%D, %M %d, %Y"     => Saturday, July 18, 1987
  1451.             "%02H:%02m i"       => 01:53 PM
  1452.             "Time now: %h%m"    => Time now: 13:53
  1453.  
  1454.  */
  1455. void
  1456. DSToStr(str,fmt,d)
  1457.     char *str, *fmt; long *d;
  1458. {
  1459.     DATE date;
  1460.     char fc,*fs,*out;
  1461.     USHORT ivalue;
  1462.     char new_fmt[256];          /* make it big to be "safe" */
  1463.     USHORT new_fmt_lng;
  1464.     char *svalue;
  1465.  
  1466.     DSToDate(d, &date);         /* convert DateStamp to DATE format */
  1467.  
  1468.     *str = '\0';                /* insure output is empty */
  1469.     out = str;
  1470.     fs = fmt;                   /* make copy of format string pointer */
  1471.  
  1472.     while (fc = *fs++) {        /* get format characters */
  1473.         if (fc == '%') {        /* formatting meta-character? */
  1474.             new_fmt_lng = 0;
  1475.             new_fmt[new_fmt_lng++] = fc;
  1476.             /* copy width information */
  1477.             while (isdigit(fc = *fs++) || fc == '-')
  1478.                 new_fmt[new_fmt_lng++] = fc;
  1479.  
  1480.             switch (fc) {       /* what are we trying to do? */
  1481.             case 'y':           /* year - 1980 */
  1482.                 ivalue = date.Dyear % 100;
  1483. write_int:
  1484.                 new_fmt[new_fmt_lng++] = 'd';
  1485.                 new_fmt[new_fmt_lng] = '\0';
  1486.                 sprintf(out,new_fmt,ivalue);
  1487.                 out = str + strlen(str);
  1488.                 break;
  1489.             case 'Y':           /* full year value */
  1490.                 ivalue = date.Dyear;
  1491.                 goto write_int;
  1492.  
  1493.             case 'm':           /* month */
  1494.                 ivalue = date.Dmonth;
  1495.                 goto write_int;
  1496.  
  1497.             case 'M':           /* month name */
  1498.                 svalue = calendar[date.Dmonth - 1].Mname;
  1499. write_str:
  1500.                 new_fmt[new_fmt_lng++] = 's';
  1501.                 new_fmt[new_fmt_lng] = '\0';
  1502.                 sprintf(out,new_fmt,svalue);
  1503.                 out = str + strlen(str);
  1504.                 break;
  1505.  
  1506.             case 'd':           /* day */
  1507.                 ivalue = date.Dday;
  1508.                 goto write_int;
  1509.  
  1510.             case 'D':           /* day name */
  1511.                 svalue = dayNames[d[0] % DAYS_PER_WEEK];
  1512.                 goto write_str;
  1513.  
  1514.             case 'h':           /* hour */
  1515.                 ivalue = date.Dhour;
  1516.                 goto write_int;
  1517.  
  1518.             case 'H':           /* hour in 12 hour notation */
  1519.                 ivalue = date.Dhour;
  1520.                 if (ivalue >= 12) ivalue -= 12;
  1521.                 goto write_int;
  1522.  
  1523.             case 'i':           /* AM/PM indicator */
  1524.             case 'I':
  1525.                 if (date.Dhour >= 12)
  1526.                     svalue = "PM";
  1527.                 else
  1528.                     svalue = "AM";
  1529.                 goto write_str;
  1530.  
  1531.             case 'n':           /* minutes */
  1532.             case 'N':
  1533.                 ivalue = date.Dminute;
  1534.                 goto write_int;
  1535.  
  1536.             case 's':           /* seconds */
  1537.             case 'S':
  1538.                 ivalue = date.Dsecond;
  1539.                 goto write_int;
  1540.  
  1541.             default:
  1542.                 /* We are in deep caca - don't know what to do with this
  1543.                  * format character.  Copy the raw format string to the
  1544.                  * output as debugging information.
  1545.                  */
  1546.                 new_fmt[new_fmt_lng++] = fc;
  1547.                 new_fmt[new_fmt_lng] = '\0';
  1548.                 strcat(out, new_fmt);
  1549.                 out = out + strlen(out);    /* advance string pointer */
  1550.                 break;
  1551.             }
  1552.         }
  1553.         else
  1554.             *out++ = fc;        /* copy literal character */
  1555.     }
  1556.     *out = '\0';                /* terminating null */
  1557. }
  1558.  
  1559. /*  FUNCTION
  1560.         StrToDS - convert a string to a DateStamp.
  1561.  
  1562.     SYNOPSIS
  1563.         int StrToDS(string, date)
  1564.             char *string;
  1565.             struct DateStamp *date;
  1566.  
  1567.     DESCRIPTION
  1568.         StrToDS expects its string argument to contain a date in
  1569.         MM/DD/YY HH:MM:SS format.  The time portion is optional.
  1570.         StrToDS will attempt to convert the string to a DateStamp
  1571.         representation.  If successful, it will return 0.  On
  1572.         failure, a 1 is returned.
  1573.  
  1574.  */
  1575.  
  1576. int
  1577. StrToDS(str, d)
  1578.     char *str; long *d;
  1579. {
  1580.     register char c;
  1581.     int count;
  1582.     int i, item;
  1583.     DATE date;              /* unpacked DateStamp */
  1584.     char *s;
  1585.  
  1586.     int values[3];
  1587.     int value;
  1588.  
  1589.     s = str;
  1590.     for (item = 0; item < 2; ++item) {  /* item = date, then time */
  1591.         for (i = 0; i < 3; ++i) values[i] = 0;
  1592.         count = 0;
  1593.         while (c = *s++) {          /* get date value */
  1594.             if (c <= ' ')
  1595.                 break;
  1596.  
  1597.             if (isdigit(c)) {
  1598.                 value = 0;
  1599.                 do {
  1600.                     value = value*10 + c - '0';
  1601.                     c = *s++;
  1602.                 } while (isdigit(c));
  1603.                 if (count == 3) {
  1604.     bad_value:
  1605. #ifdef DEBUG
  1606.                     puts("Error in date-time format.\n");
  1607.                     printf("at %s: values(%d) = %d, %d, %d\n",
  1608.                         s, count, values[0], values[1], values[2]);
  1609. #endif
  1610.                     return 1;
  1611.                 }
  1612.                 values[count++] = value;
  1613.                 if (c <= ' ')
  1614.                     break;
  1615.             }
  1616.             else if (! index(DATE_SEPARATORS, c) )
  1617.                 goto bad_value;     /* Illegal character - quit. */
  1618.         }                           /* end while */
  1619.         if (item) {                 /* Getting time? */
  1620.             date.Dhour = values[0];
  1621.             date.Dminute = values[1];
  1622.             date.Dsecond = values[2];
  1623.         }
  1624.         else {                      /* Getting date? */
  1625.  
  1626. /* It's OK to have a null date string, but it's not OK to specify only
  1627.    1 or 2 of the date components.
  1628.  */
  1629.             if (count && count != 3)
  1630.                 goto bad_value;
  1631.             date.Dmonth = values[0];
  1632.             date.Dday = values[1];
  1633.             date.Dyear = values[2];
  1634.             if (date.Dyear == 0) {
  1635.                 date.Dyear = START_YEAR;
  1636.                 date.Dday = 1;
  1637.             }
  1638.             else {
  1639.                 if (date.Dyear < (START_YEAR - 1900) )
  1640.                     date.Dyear += 100;
  1641.                 date.Dyear += 1900;
  1642.             }
  1643.         }
  1644.     }                               /* end for */
  1645.     DateToDS(&date, d);
  1646.     return 0;
  1647. }                                   /* StrToDS */
  1648.  
  1649.  
  1650. #ifdef DEBUG
  1651. #include "stdio.h"
  1652. main(ac, av)
  1653.     int ac;
  1654.     char    **av;
  1655. {
  1656.     long    datestamp[3];           /* A little dangerous with Aztec */
  1657.     long    datestamp2[3];
  1658.     DATE    date, oldDate;
  1659.     long    day, lastDay;
  1660.     int     errors = 0;
  1661.  
  1662.     /*
  1663.      * display results from DateStamp() (hours:minutes:seconds)
  1664.      */
  1665.     DateStamp(datestamp);
  1666.  
  1667.     /*
  1668.      * display results from DSToDate() (e.g. "03-May-88")
  1669.      */
  1670.     DSToDate(datestamp, &date);
  1671.     printf("Current date: %02d-%s-%02d\n",
  1672.         date.Dday, calendar[ date.Dmonth - 1].Mname,
  1673.         (date.Dyear % YEARS_PER_CENTURY));
  1674.  
  1675.     printf("Current time: %02d:%02d:%02d\n",
  1676.         date.Dhour, date.Dminute, date.Dsecond);
  1677.  
  1678.     printf("\nDoing sanity check through year 2000...\n\t");
  1679.     lastDay = (2000L - START_YEAR) * 365L;
  1680.     lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP;
  1681.     for (day = 0; day <= lastDay; ++day) {
  1682.         if (day % 1000 == 0) {
  1683.             printf(" %ld", day);
  1684.             fflush(stdout);
  1685.         }
  1686.         datestamp[0] = day;
  1687.         datestamp[1] = MINS_PER_HOUR - 1;
  1688.         datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1);
  1689.         DSToDate(datestamp, &date);
  1690.         if (day && date == oldDate) {
  1691.             printf("Got same date for days %d, %d: %02d-%s-%02d\n",
  1692.                     day - 1, day,
  1693.                     date.Dday,
  1694.                     calendar[ date.Dmonth - 1 ].Mname,
  1695.                     (date.Dyear % YEARS_PER_CENTURY));
  1696.  
  1697.             if (++errors == 10)
  1698.                 exit(1);
  1699.         }
  1700.         DateToDS(&date, datestamp2);
  1701.         if (day != datestamp2[0]) {
  1702.             printf("\nConversion mismatch at day %ld!\n", day);
  1703.             printf("\tBad value = %ld", datestamp2[0]);
  1704.             printf("\tDate: %02d-%s-%02d\n",
  1705.                     date.Dday,
  1706.                     calendar[ date.Dmonth  -1 ].Mname,
  1707.                     (date.Dyear % YEARS_PER_CENTURY));
  1708.             if (++errors == 10)
  1709.                 exit(1);
  1710.         }
  1711.         oldDate = date;
  1712.     }
  1713.     printf("\nSanity check passed.\n");
  1714. } /* main() */
  1715. #endif
  1716.  
  1717. SHAR_EOF
  1718. cat << \SHAR_EOF > MRDates.h
  1719. /* MRDates.h - Declarations for types and variables used by MRDates. */
  1720.  
  1721. typedef struct {
  1722.     int    Dyear;        /* year AD (e.g. 1987)    */
  1723.     int    Dmonth;        /* month of year (0-11)    */
  1724.     int    Dday;        /* day in month (1-31)    */
  1725.     int Dhour;        /* 0-23                 */
  1726.     int Dminute;    /* 0-59                 */
  1727.     int Dsecond;    /* 0-59                 */
  1728.     int    Dweekday;    /* day of week (Sun=0)    */
  1729. } DATE;
  1730.  
  1731. typedef struct {
  1732.         char    *Mname;
  1733.         int     Mdays;
  1734.         } CalEntry;
  1735.  
  1736. #ifdef MRDATES
  1737. CalEntry calendar[12] = {
  1738.         { "Jan", 31 },   { "Feb", 28 },  { "Mar", 31 }, { "Apr", 30 },
  1739.         { "May", 31 },   { "Jun", 30 },  { "Jul", 31 }, { "Aug", 31 },
  1740.         { "Sep", 30 },   { "Oct", 31 },  { "Nov", 30 }, { "Dec", 31 }
  1741.     };
  1742. #else
  1743. extern CalEntry calendar[12];
  1744. #endif
  1745.  
  1746. #ifdef MRDATES
  1747. char *dayNames[7] = {
  1748.     "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
  1749.     };
  1750. #else
  1751. extern char *dayNames[7];
  1752. #endif
  1753.  
  1754. SHAR_EOF
  1755. cat << \SHAR_EOF > Makefile
  1756. # Makefile for DiffDir program
  1757.  
  1758. CFLAGS = -n
  1759.  
  1760. OBJ = DiffDir.o MRDates.o
  1761.  
  1762. DiffDir: $(OBJ)
  1763.     ln -w -g -o DiffDir $(OBJ) -lc
  1764.  
  1765. clean:
  1766.     delete (#?.o|#?.dbg)
  1767.  
  1768. SRC = DiffDir.c MRDates.h MRDates.c Makefile
  1769. BIN = DiffDir.DOC DiffDir Sample.Output
  1770.  
  1771. zoo: $(SRC) $(BIN)
  1772.     zoo a DiffDir $(SRC) $(BIN)
  1773.  
  1774.  
  1775. SHAR_EOF
  1776. cat << \SHAR_EOF > Sample.Output
  1777. *** 'dir1/DifferentType'  is a directory
  1778. *** but 'dir2/DifferentType'  is a file!
  1779. *** Mismatch:  comments 
  1780. dir1/
  1781. 12-31-88 15:48:41 -----rwed       18 File2
  1782. : This is file 2 in level 1 of dir 1.
  1783. ------------------------------------
  1784. dir2/
  1785. 12-31-88 15:48:41 -----rwed       18 File2
  1786. ====================================
  1787. *** Mismatch:  names 
  1788. dir1/
  1789. 12-31-88 15:48:53 -----rwed       18 File3
  1790. ------------------------------------
  1791. dir2/
  1792. 12-31-88 15:48:53 -----rwed       18 file3
  1793. ====================================
  1794. *** Mismatch:  dates  sizes 
  1795. dir1/Level2/
  1796. 12-31-88 15:53:10 -----rwed       81 File1
  1797. ------------------------------------
  1798. dir2/Level2/
  1799. 12-31-88 15:48:33 -----rwed       18 File1
  1800. ====================================
  1801. *** File missing: 'dir1/Level2/File2'
  1802. *** File missing: 'dir2/Level2/Level3/File1'
  1803. SHAR_EOF
  1804. #    End of shell archive
  1805. exit 0
  1806. -- 
  1807. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  1808. Have five nice days.
  1809.